Java NIO Buffer

.基本的Buffer用法

.Buffer容量、位置、及 限制

.Buffer的类型

.分配一个Buffer

.把数据写入到buffer中

.flip()

.从Buffer中读数据

.rewind()

.clear() 和compact()

.mark() 和reset()

.equals()和compareTo()


一、基本Buffer用法

java NIO Buffer经常用于和NIO Channel打交道。正如你所知的那样,数据从channel读取到buffer中以及从buffer中写入到channel中。

//即:Buffer是数据和Channel的桥梁

buffer在本质上就是一个内存块,你可以把数据写入到buffer中。之后,你还能从buffer中再次读取数据。这个内存块被封装成一个NIO Buffer对象。这个Buffer对象提供了一组操作内存块的便捷方法。

在使用Buffer读写数据时,通常遵循下面四个步骤:

    1.把数据写入到buffer中

    2.调用buffer.flip()方法

    3.从buffer中把数据读出来

    4.调用buffer.clear()或buffer.compact()

当你写入数据到buffer中时,buffer会记录下你已经写了多少数据。一旦你想读取数据时,你就可以调用flip()方法把buffer从写模式(writing mode)切换到读模式(reading mode)。在读模式下,buffer允许你读取所有被写入到buffer中的数据。

一旦你读取了所有的数据后,你就需要清空一下这个buffer,这样它就能再次把数据写入到buffer中。你有两种方式做到这一点:1.是调用clear()方法,2是调用compact()方法。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 Capacity, Position 以及 limit

Buffer本质上是一个可以写入数据的内存块,之后,你又能再次从中读取数据。这个内存块被封装成一个NIO Buffer对象。它提供了一系列操作内存块的方法。

一个Buffer有三个属性,通过它们你可以理解一个Buffer是如何工作的:

capacity

position

limit

position和limit的意义取决于Buffer是处于read模式还是处于write模式。而无论在那种模式下,capacity都是相同的。

这里有一个关于 capacity、position、limit在写和读模式下的的图例。详细地解释会在图例之后的下一个章节讲述。


Java NIO Buffer_第1张图片
Buffer capacity, position and limit in write and read mode


Capacity(容量)

作为一个内存块,Buffer都有一个确定大小,也被称为它的容量。你只能写capacity大小个字节的数据,long类型,char类型等等。一旦Buffer满了之后,在你准备向该buffer中写入更多数据之前,你需要先清空它(读取其中的数据或清除它)。


Postition

当你把数据写入到Buffer中时,你会在一个确定的位置操作。最初始的位置是0。当一个byte,long类型等的数据已经被写入到Buffer中时,位置position就会前进到buffer中的下一个位置处。Position的最大值是capacity-1。

  当你从一个Buffer中读取数据时,你也是从一个给定位置处开始的。 当你flip一个Buffer时,会使其从写模式(writing mode)切换到读模式(reading mode),同时position被重置回0。当你从Buffer中读数据时,你会从position位置处开始读,并且position还会前进到下一个要读取的位置。 //这里的position类似于指针。

Limit

在写模式下,一个Buffer的limit就是你总共能向这个buffer中写入多少数据。在写模式下,limit就等价于Buffer的容量。

当把一个Buffer切换到读模式下时,limit就意味着你能从该Buffer中读取多少数据。因此,当flip一个buffer到读read模式下时,limit就被设置为写模式writeMode时的写位置write position。换句话说,你写入多少字节的数据,你就能读取多少字节的数据(limit被设置为 写入数据的字节数,被position所标识)。


三、Buffer类型

Java NIO提供了下面几种Buffer类型:

       .ByteBuffer

      .MappedByteBuffer

      .CharBuffer

      .DoubleBuffer

      .FloatBuffer

      .IntBuffer

      .LongBuffer

      .ShortBuffer

正如你看到的,这些Buffer类型分别代表了不同的数据类型。换句话说,它们允许你在Buffer中操作char,short,int,long,float或double类型的数据。

MappedByteBuffer稍微有点不同,我们将在一个独立的章节中讲述它。


四、分配一个Buffer

要获取一个Buffer对象,你首先需要allocate一个。每一个Buffer类都有一个allocate()方法。

下面的这个例子展示了 如何allocate一个48字节的ByteBuffer:

  ByteBuffer buf=ByteBuffer.allocate(48);

下面的例子,则是分配了一个1024个字符大小的CharBuffer类型的buffer:

CharBuffer buf = CharBuffer.allocate(1024);


五、向Buffer中写入数据

有俩种方式可以向Buffer中写入数据:

  1.从Channel中把数据写入到一个Buffer中。

  2.向Buffer自身中写入数据,通过buffer的put()方法。

下面的这个例子演示了一个Channel如何把数据写入到一个Buffer中:

int bytesRead = inChannel.read(buf); //read into buffer.

下面的例子则是通过put()方法向Buffer中写入数据:

buf.put(127);

还有许多其他的put()方法,可以让你以不同的方式向Buffer中写入数据。例如,在特定位置写入,或这把一个字节数组写入到buffer中。要想获取更多细节,请查看具体buffer实现的JavaDoc文档。


flip()

flip()方法会把一个Buffer从写模式切换到读模式。调用flip()方法会把position的值设置回0同时把limit的值设置成刚刚position所处的位置。换句话说,现在position标记读位置,limit标记了有多少字节,字符等被写进到该buffer中。-可以读取的字节,字符等的上限。




六、从Buffer中读取数据

有俩种方式从Buffer中读取数据:

1.从buffer中读取数据到一个channel中。

2.从buffer本身中读取数据,使用get()方法。

下面有个例子展示了如何从buffer中读取数据到一个channel中:

//read from buffer into channel.

int bytesWritten = inChannel.write(buf);

下面的这个例子,展示了使用get()方法从buffer中读取数据:

byte aByte = buf.get();

还有很多重载的get()方法,它们允许你以许多不同的方式从Buffer中读取数据。例如,从指定位置处读,或 从buffer中读取一字节数组的数据。

rewind()

Buffer.rewind()方法会把position设置回0,因此你可以重新读取buffer中的所有数据。limit的值会保持原样,因此,limit的值仍然标识着可以从Buffer中读取多少元素(字节,字符,等等)。

clear()和compact()

一旦你完成从Buffer读取数据之后,你就不得不让该buffer为再次的写入做好准备。通过调用clear()或compact()方法做到这一点。如果你调用了clear()的话,position会被设置为0,limit会被设置为容量大小。换句话说,该Buffer被清除了,Buffer中的数据并没有被清除。这些标记只是告诉你该把数据写到buffer的什么地方。

  当你调用clear()方法时,如果buffer中还有其他未读取的数据,那么那些数据就会被"遗忘",也就是意味着:不再有任何标记可以告诉你那些数据已经被读取了,那些数据还没有别读取。

  如果Buffer中尚有未读取的数据,并且你希望在以后再读取它,但是你又需要先做一些其他的事情,这时,你就应该调用compact()而不clear()。

  compact()方法会把所有尚未读取的数据复制到Buffer的起始处。之后,把position的值设为最后一个未读取数据元素的后面。limit此时仍然被设置为capacity的大小。就像clear()方法一样。现在,该Buffer就准备好写入操作了。但是这样做的话,你不会在写入时覆盖未读取的数据。


mark()和reset()

通过调用Buffer.mark()方法,你可以标记一个Buffer中的给定位置。这样,在之后,你就可以调用Buffer.reset()方法来把position的值设置回曾经标识过的位置。

下面有一个例子:

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只有在下面的条件满足时才相等:

        1.它们是同一类型的Buffer(例如,字节,char,int等等)

        2.它们在buffer中剩余的字节数量(或字符数量)相等。

        3.所有剩余的字节,字符等都相等。

  我们可以看到,equals()比较的仅仅是Buffer中的一部分,而不是它内部的每一个单一元素。事实上,它仅仅比较Buffer中剩余的元素。

compareTo()

  compareTo()方法会比较两个buffer中的剩余元素(如,字节,字符等), 在下面这些情况下,一个buffer会被视为比另一个“小”:

    1.buffer中的第一个元素(与另一个buffer中的相应元素相等的那个) 比另一个buffer中的要小。

    2.所有的元素都相等,但是第一个buffer在第二个buffer进行之前已经用完了元素(它里面的元素很少)

你可能感兴趣的:(Java NIO Buffer)