Buffer是用来跟Channel进行交互的。正如你所知道的,数据都是从Channel读到Buffer中的,并从Buffer写入到Channel中的。
Buffer本质上是一块内存空间,你可以将数据写入到这里,并在之后读取它。这个内存块被包装成NIO Buffer对象,并提供了一系列的方法来方便对这个内存块数据的操作。
Buffer基本用法
使用Buffer来读写数据基本上按照以下几个步骤来进行。
- 将数据写入到Buffer
- 调用buffer.flip()方法。
- 从buffer中读取数据。
- 调用buffer.clear()或buffer.compact()方法。
当你向Buffer中写入数据时,buffer会跟踪有多少数据已经写入。当需要读取数据时,你需要buffer.flip()方法将buffer从写模式切换到读模式。在读模式中,你可以读取所有已经写入到Buffer中的数据。
当你已经读完buffer中的数据,你需要清空buffer来保证buffer能够再次被写。清空buffer有两种方法:clear()方法和compat()方法。clear()方法会清空所有的buffer中的数据,compat()方法只会清空已经读过的数据,所有没有读过的数据会放到buffer的最前面,新写入的数据会追加到未读数据的后面。
这是一个Buffer使用的简单例子,包括write,flip,read和clear方法。
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");FileChannel inChannel = aFile.getChannel();//create buffer with capacity of 48 bytesByteBuffer 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 Capacity, Position and Limit
Buffer本质上是一个可以进行读写操作的内存区域。这块内存区域被包装成Java Buffer对象,它包含了一系列的方法来简化对内存数据块的操作。
一个Buffer包含三个主要属性必须要熟悉。
- capacity
- position
- limit
position和limit所代表的意义跟Buffer的读写模式有关,capacity则跟读写模式无关。
下图说明的是capacity,position和limit在读写模式中所代表的意义。
Capacity
作为一个内存块,Buffer有固定的大小,叫做capacity。你最大只能写入capacity个bytes,longs,chars个数据到Buffer。一旦buffer满了,你必须清空它(从buffer中读取数据或调用clear方法)。
Position
当你向缓存中写入数据,position表示当前位置。初始的position为0,当一个byte,long等被写入到buffer中时,poistion会移动到下一个可写的数据单元。position的最大值为capacity-1。
从Buffer中读取数据也是从指定的position开始。当使用flip()方法将buffer的模式从写模式切换到读模式,position位置变为0。数据从position指定的位置开始读起,并移动到下一个可读的单元。
Limit
在写模式中,limit指定了有多少数据可以写入到buffer中。在写模式中,limit与capacity值相等。
当buffer切换到读模式时,limit指定了buffer中可读的数据的大小。因此,当buffer切换到读模式时,limit被设置为写模式时的position位置。换句话说,读的数据可达到写的数据一样。
Buffer类型
Java NIO中包含以下几种类型。
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
正如你所看到的,这些Buffer代表不同的数据类型。换句话说,可以通过char,short,int,long,float 或 double类型来操作缓冲区中的字节。
分配Buffer
在获得Buffer对象之前必须先分配。每一个Buffer类都有一个allcate()方法来实现。下面是一个ByteBuffer分配48个字节容量的buffer的例子。
ByteBuffer buf = ByteBuffer.allcate(48);复制代码
下面是一个CharBuffer分配1024个字符的例子:
CharBuffer buf = CharBuffer.allcate(1024);复制代码
向Buffer中写入数据
向Buffer中写入数据有两种方式: 1、通过Channel向Buffer中写入数据。 2、通过buffer.put()方法写入。 下面是一个通过Channel向Buffer中写入数据的例子。
int byteRead = inChannel.read(buf); // read into buffer复制代码
下面是一个通过Buffer的put()方法写入数据的例子:
buf.put(127);复制代码
put还有其他版本的方法。比方说,在指定的位置写入数据,向buffer中写入一个数组。更多实现细节参考JavaDoc。
flip()
flip()将buffer从写模式切换到读模式。调用flip()方法将position重置为0,并将limit设置为原来position原来的位置。
换句话说,position表示当前读的位置,limit表示目前可读的数据。
从Buffer中读取数据
从Buffer中读取数据有两种方式
- 将Buffer中的数据读入到Channel。
- 通过Buffer的get()方法读取。
下面是一个将数据从Buffer读取到Channel中的例子。
// read from buffer into channelint byteWritten = inChannel.write(buffer);复制代码
下面是一个通过Buffer的get方法读取数据的例子
byte aByte = buf.get();复制代码
get方法还有很多其他的版本。例如,从指定的位置开始读,从buffer中读取一个数组。更多实现细节参考JavaDoc。
rewind()方法
Buffer.rewind()方法将position重置为0,因此,可以从头开始读取Buffer中的数据。limit的值保持不变,仍然表示最多可读取的数据量。
clear()和compat()方法
一旦完成从Buffer中读取数据,需要让Buffer准备好再次写入,可以通过clear()方法和compat()方法来完成。
如果调用的是clear()方法,position会重置为0,并且limit重置为capacity的值。换句话说,Buffer被清空了。Buffer中的数据并没有被清空,只是这些标记告诉我们从哪里开始可以写入数据。
当Buffer中还剩下一些未读取的数据时调用了clear()方法,这些数据将会被忽略。这意味着不再有任何标记告诉我们哪些数据已经读取过了,哪些数据还未读取过。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
mark方法与reset方法
通过调用Buffer.mark()方法可以标记一个指定的位置。通过Buffer的reset()方法可以回到原来mark时的position位置,下面是一个例子:
buffer.mark();// call buffer.get() a couple of times. buffer.reset();复制代码
equals方法和compareTo方法
可以通过equals方法和compareTo方法来比较两个buffer。
equals方法
- 有相同的类型(byte、char、int等)。
- Buffer中剩余的byte、char等的个数相等。
- Buffer中所有剩余的byte、char等都相同。
正如你所见,equals仅比较了Buffer中的部分数据,而不是Buffer中的每一个元素。事实上,它仅仅比较了剩余的元素。
compareTo方法
compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:
- 第一个不相等的元素小于另一个Buffer中对应的元素 。
- 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。