- Basic Buffer Usage
- Buffer Capacity, Position and Limit
- Capacity
- Position
- Limit
- Buffer Types
- Allocating a Buffer
- Writing Data to a Buffer
- flip()
- Reading Data from a Buffer
- rewind()
- clear() and compact()
- mark() and reset()
- equals() and compareTo()
- equals()
- compareTo()
NIO buffers和 channels交互使用。正如你所知,数据总是从channel 读入buffer,从buffer写入channel。
buffer本质上是一个你可以先写入,然后读出数据的内存块,这个内存块用NIO buffer对象包装了起来以方便使用。
基本buffer类用法
使用Buffer类读写数据典型的套路写法有四步:
- 读数据到Buffer
- 调用buffer.flip()
- 从buffer中读出数据
- 调buffer.clear() 、buffer.compact()方法
【译注: 读写buffer,是以buffer为主体的。写模式:实际是buffer.read() ,即将数据从channel读入buffer; 读模式,实际是buffer.write() ,即将数据从buffer读出写入channel】
写数据到buffer的时候,buffer会记录程序写入了多少数据。一旦开始读数据了,需要使用flip()从写模式切换到读模式。读模式允许你把所有已写的数据读出来。
一旦读完所有数据,需要clear(清理)buffer,以待再次写,NIO提供了两种方式:
clear()或者compact()。Clear()方法会“清理”掉buffer中所有内容;compact()仅仅会“清理“掉已读的内容,而所有未读内容会被移动到buffer内容器(数组)开始的一端,以后数据会被追加写到该buffer。
下面是一个简单的buffer使用案例:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf); //读出数据写入到buffer (写模式)
while (bytesRead != -1) {
buf.flip(); //buffer正待被读出 (切换为 读模式 )
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // 逐个字节读出
}
buf.clear(); //buffer正待被写入
bytesRead = inChannel.read(buf);
}
aFile.close();
Buffer Capacity(容量), Position(位置) and Limit(上界)
Buffer 这个内存块有三个需要重点熟悉的属性:
- capacity
- position
- limit
position 、 limit 的含义取决于Buffer是读、还是写模式。capacity在这两种模式下倒是含义相同。
下图解释了这三个属性在读、写模式下的模型:
capacity
buffer作为内存块,有着固定的内存大小,也称“capacity”。程序写capacity这个容量的字节数据、长整型、字符数据到buffer里。buffer满了以后,如果想继续写入数据,需要首先清空它(将数据读出来,或者清理掉)
position
程序是在一个特定的位置(position)把数据写入buffer的。初始的position=0。写入一个byte、long数据到buffer后,position指针会向后移动一位。position指针最大=capacity-1.
从buffer读数据时也是从一个特定position开始的。当程序flip()一个buffer,将模式从写改为读时,position被重置为0。当你从buffer读取数据时,也会移动position指针到下一个要读的位置。
limit
在Buffer的写模式下,一个Buffer的limit指程序最多能往buffer中写入多少数据,此时limit= buffer的capacity。
在buffer的读模式下,limit 指能从buffer读取的最多数据。因此,flip一个buffer变成读模式后,limit 被置为 写模式下position的位置。换言之,程序写了多少数据最多就可以读多少数据(limit 被置为已写的字节数,这也被称为“Mark”)
Buffer的类型
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
Buffer有多种类型可供使用,MappedByteBuffer有点特殊,容后详解。
buffer对象内存分配(allocate)
实例化一个Buffer对象首先要使用allocate()方法为其分配内存。下面是一个容量为48个字节的ByteBuffer对象内存是如何分配的.
ByteBuffer buf = ByteBuffer.allocate(48);
这里有一个容量为1024个字符的CharBuffer内存是如何分配的:
CharBuffer buf = CharBuffer.allocate(1024);
向Buffer写入数据
有两中方式向Buffer中写入数据:
- 1 通过Channel向Buffer写入数据
- 2 通过Buffer#put()方法 手动向Buffer写入数据
下面是如何通过Channel向Buffer写入数据的:
int bytesRead = inChannel.read(buf);
这里是展示如何手动向Buffer写入数据的:
buf.put(127);
put()有很多实现,写数据到Buffer的方式也各式各样。比如说,写入特定的position间数据,或者将一组字节写入到Buffer。可参考JavaDOC获取更多细节。
flip()
Flip()方法将一个Buffer 从写模式转为读模式。 调用flip()把position置为0,并把limit置为原position所在位置。
换言之:此时,position 标志了起读的位置(0),limit 则说明可以最多写多少字节、字符等数据到Buffer里。
从Buffer读取数据
从Buffer中读取数据有两种方式:
- 1、将Buffer中数据读到Channel
- 2 、用get()获取buffer中数据
下面是如何将数据从Buffer读到Channel:
int bytesWritten = inChannel.write(buf);
这个是如何使用get()获取Buffer中数据:
byte aByte = buf.get();
get()方法也有多种实现。比如,从特定position间读,或者从buffer中读取字节数组,等等。
可查看JavaDOC获取详情。
rewind()– (就是多次读buffer所需操作)
(英文原指:rewind-倒带,退卷)
Buffer .rewind()方法将position指针置为0,这样可以**重读**buffer中所有数据。limit 不变,仍然可以标志最多可以从Buffer中获取多少字节、字符等。
clear()、compact()
从Buffer中读完数据后,必须重设Buffer的状态才能写入数据。可以通过clear() compact()做到。
调用clear()方法,position指针置为0,limit = capacity。换言之:Buffer被“clear”了,但实际上Buffer中数据并没有被真正“clear”掉,真正被“clear”掉的是那些表明从哪里可以清除数据的标志(markers).
调用clear()时如果buffer里还有未读数据,这些数据将会被“遗忘”掉,这意味着将没有标志(markers)说明哪些数据已读、哪些未读。
如果Buffer里还有未读数据,而且你后续想读,只是现在需要写入一些数据的话,调用compact(),而不是clear()。
compact()会把未读数据copy到Buffer内存块(数组)。然后,把position置为未读内容的最后元素所在处。limit仍被置为capacity,就像clear()一样。这样,Buffer内就可继续写数据,但你不会覆盖掉未读的数据了。
Mark()、reset()
你可以通过Buffer.mark() “mark”一下 buffer中某个position。然后通过Buffer.reset() 把position重设到 mark的位置。示例:
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.
equals() 、compareTo()
可以通过equals()、compareTo()方法比较两个buffer。
equals():
两个buffer在满足下列条件下是equal的:
- 类型相同(byte 、char等)
- 两者剩余相同数量的bytes、chars 等
所有剩余的字节、字符都是equal的
如你所见,equals()只比较Buffer的部分元素,而不是逐个比较buffer中的单个元素。事实上,本方法只比较Buffer中剩余元素。
CompareTo()
CompareTo() 比较两个buffer的 剩余元素(bytes 、chars等),比如在排序规则中会调用本方法。一般很少用到。