Java NIO Buffers被用于和NIO Channels交互,正如你所知道的,数据是从channels中读取到Buffers中,从Buffers中写入到channels。
Buffer本质上是一块你可以读写数据的内存,这块内存被NIO Buffer 对象包装,这个对象提供了一系列的方法来使得和内存的交互更简单。
使用Buffer通过下面四个步骤来读写数据
当你写数据到Buffer中的时候,Buffer会记录你写了多少数据。一旦你打算读取写到Buffer中的数据,你需要通过调用flip()方法将Buffer从读模式切换到写模式。在读模式的Buffer中,你可以读取所有写到Buffer中的数据。
一旦你已经读取了写在Buffer中的所有数据,你需要清除Buffer,让他去准备接下来数据的再次写入。你可以通过调用clear()或者compact()方法来清除Buffer。clear()方法会清楚整个Buffer,compact()只是清除已经读过的数据。那些还没有读取的数据被移动到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); //read into buffer.
while (bytesRead != -1) {
buf.flip(); //make buffer ready for read
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // read 1 byte at a time
}
buf.clear(); //make buffer ready for writing
bytesRead = inChannel.read(buf);
}
aFile.close();
Buffer本质上是一块你可以读写数据的内存,这块内存别NIO Buffer对象包装,它提供了一系列的方法来是的内存中的数据读写变得更简单。
为了理解一个Buffer怎么工作运行,你应该熟悉的三个属性
- capacity
- position
- limit
position和limit属性的具体意义是和Buffer的读写模式有关的,但是Capacity是和Buffer的读写模式没有关系的。
下图是一个关于capacity,position,limit在Buffer读写模式不同的解析图,在图片底下有具体的解释说明
作为一个内存块,Buffer有一个固定的大小,他也被称为容积,你可以写bytes,longs,chars等其他数据格式到Buffer中,一旦Buffer被写满。你必须在下一次写入数据之前清空Buffer。
当你写入数据到Buffer中,position表示当前位置。position的初始位置是0。当一个byte,long或者其他数据被写到Buffer中,position指向buffer接下来插入数据的格子。position中的最大值是capacity-1。
当你读取数据的时候,也是从某个特定的位置开始读取。当你将Buffer从写模式切换到读模式。position将会被重置到0位置,当你在Buffer中读取数据的时候。position也会指向下一个即将读取的位置。
在写模式中,Buffer中的limit是指限制你向Buffer中写入数据的大小。在写模式中,limit是和Buffer的capacity是等效的。
当切换Buffer为读模式的时候。limit是指你可以从Buffer中读取数据的大小。因此,当Buffer从写模式切换到读模式的时候,limit被设置为写模式的写入位置。换句话说,你可以从Buffer中读取写入到Buffer中相等的数据。
java NIO中包含下面这几种类型的Buffer
- ByteBuffer
- CharBuffer
- MappedByteBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
正如你所看到的,这些Buffer类型代表着不同的数据类型。换句话说,就是通过char,short,int,long,float,double来操作缓冲区的字节数据。
MappedByteBuffer 是有一点特殊的,这个会在后面的文章中学习。
为了得到一个Buffer对象,你必须先分配他。每个Buffer类都有一个allocate()方法来进行分配。下面展示了分配一个容积为48字节的ByteBuffer。
ByteBuffer buf = ByteBuffer.allocate(48);
这是分配一个大小为1024字符的CharBuffer的例子
CharBuffer buf = CharBuffer.allocate(1024);
你可以用下面这两种方式来向Buffer中写入数据
下面展示了如何通过Channel来向Buffer中写入数据
int bytesRead = inChannel.read(buf); //read into buffer.
下面展示通过调用put方法来向Buffer中写入数据
buf.put(127);
这里有一些put()方法的其他版本,这些不同的版本是以不同的方式将数据写入到Buffer中,例如写入到一个特定的位置,或者将一个字节数组写入到Buffer中,具体细节请参见javadoc文档。
flip()方法将Buffer从写模式转换成读模式,调用flip()方法设置position为0,同时设置limit为刚刚position的位置。
换言之,postion现在是标记数据读取位置,limit标记是多少bytes,chars。。被写入到Buffer中。可以从Buffer中读取多少bytes,chars。
下面是两种从Buffer中读取数据的方式
使用get()方法从Bufffer中读取数据。
下面展示怎样从Buffer中读取数据到channel
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
下面展示通过get()方法来读取数据
byte aByte = buf.get();
这里有一些其他版本的get()方法。通过这些方法,你可以以不同的方式从Buffer中读取数据。例如,从一个特定位置读取数据,或者从Buffer中读取一个字节数组,具体细节请参见javadoc文档。
rewind()方法使得position位置回归到0位置,因此你可以重新再读取一边Buffer中的数据。limit保持不变,因此依旧标记了多少可以从Buffer中读取的数据。
一旦你从Buffer中读取数据之后,你要确保Buffer准备再次写入数据。你可以通过调用下面的clear()和compact()两种方法来实现这个功能。
如果你调用clear(),position将会被设置为0,limit被设置为capacity.换言之,Buffer被清除。在Buffer中的数据没有被清除,仅仅告诉你可以写入数据到Buffer的标志位。
当你调用clear()方法的时候。如果Buffer中有一些数据没有被读取。那些没被读取的数据将会被遗忘,这也就意味着这不再有标志位来标记哪些数据已经被读哪些数据没有被读取。
如果在Buffer中有一些数据没有被读取,你想后来再读取,但是首先需要做一些写入。这时候你应该调用compact()方法而不是clear()。
compact()方法会复制那些没有被读取的数据到Buffer的开端位置,然后设置position在buffer中最后一个未读元素的正后方。limit属性就和执行clear()一样依旧被设置成capacity。现在buffer可以准备写入数据了,但是那些未读的数据不会被覆盖。
你可以在Buffer中通过调用Buffer.mark()标志一个position。你可以在后面通过调用reset()方法来重置这个postion到用mark()方法标志的位置。
下面是一个例子
buffer.mark();
//这里中间可以有其他操作
buffer.reset();
可以通过调用equals()方法和compareTo()方法来比较两个Buffer。
equals()方法
如果两个Buffer满足下面的条件,则这两个Buffer相等
正如你所见,equals()仅仅是比较两个Buffer中的一部分。不是里面的每一个元素,事实上,他只是比较两个Buffer中剩余的元素。
compareTo()方法
compareTo是用来比较两个Buffer中剩余的元素(bytes,chars。。),如果满足下面的条件,一个Buffer可以认为比另一个小。
原文链接:Java NIO Buffer