Java NIO-3.Buffer

Java NIO Buffers用来和NIO Channels交互。正如前文所述,数据从通道中读到缓冲区,或者从缓冲区写到通道。
缓冲区本质上是一块能写入数据,并延迟读取的内存。这块内存被包装成一个NIO Buffer类,并提供了一组方法简化对它的访问。

Buffer的基本用法

对Buffer的读写一般遵循以下四个步骤:

  1. 写入数据到Buffer
  2. 调用buffer.flip()方法
  3. 从Buffer中读取数据
  4. 滴啊用buffer.clear()或者buffer.compact()方法

向缓冲区写入数据时,缓冲区记录写入了多少数据。一旦要读取数据,需要调用flip()方法将缓冲区从写模式转换到读模式。在读模式下,可以读取到之前写入缓冲区的数据。

完成数据读取后,需要清空缓冲区,让它可以再次被写入。有两种方式能够清空缓冲区:调用clear()方法或者compact()方法。clear()方法清空整个缓冲区。compact()方法仅清空已经读取的部分。所有未读取的数据被移动到缓冲区的开头。新数据将从未读取数据的后面开始写入。

以下是一个使用Buffer的简单实例,注意其中的write,flip,read,clear操作(原文中意为这四个操作用黑体标识,但是不支持)。

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

缓冲区本质上是一块可以写入数据,然后从中读取的内存。这块内存被包装成一个NIO Buffer对象,提供了一组方法用来简化对内存的访问。
为了了解Buffer的工作原理,需要熟悉它的三个属性:

  • capacity
  • position
  • limit
    position和limit的含义取决于Buffer处在读膜还是还是写模式。capacity的含义是不变的。
    以下是一个关于capacity,position,limit在读写模式中的插图,详细的解释在插图后面:


    Java NIO-3.Buffer_第1张图片
    Buffer capacity,postion and limit in write and read mode

Capacity

作为一个内存块,Buffer有固定的大小,叫做"capacity(容量)"。只能往里面写入capacity个bytes,long,chars等类型。一旦Buffer满了,需要清空它才能继续往里面写入数据。

Position

数据写入Buffer中时,需要知道一个确切的位置。初始位置是0,当一个byte,long等数据写入后,position移动到buffer中的下一个插入数据的位置。position最大可为capacity-1.
从buffer读取数据时,也需要从某个特定的位置读。当buffer被从写模式flip成读模式时,position重置为0.当从Buffer中读数据时,就从position开始,然后position往后移动到下一个读的位置。

Limit

在写模式中,Buffer的Limit是对能写入多少数据的限制。写模式中limit等于Buffer的容量(capacity)
当把Buffer 转换到读模式后,limit代表能从buffer中读取多少数据。所以,buffer转换到读模式时,limit被设定为写模式中写入的位置。也就是说,能从buffer中读取的字节数就等于它被写入的字节数(limit被设定为写入的字节数,在写模式中就是position。)

Buffer Types

Java NIO 中有以下Buffer类型:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
    可以看到,这些Buffer类型代表了不同的数据类型。换句话说,可以通过char,short,int,long,float或者double来操作buffer中的字节。

Buffer的分配

为了获得一个Buffer对象,需要先对它进行分配(allocate),每一个Buffer对象都有allocate()方法来完成这个工作。
例,分配了容量为48字节的ByteBuffer:

ByteBuffer buf = ByteBuffer.allocate(48);

例,分配容量为1024个字符的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

向Buffer中写

向Buffer中写入数据有两种方法:

  1. 从Channel中写到Buffer
  2. 通过缓冲区的put()方法写入数据。
    例,从Channel写到Buffer:
int byteRead = inChannel.read(buf);// read into a buffer

例,通过put方法写入:

buf.put(127)

put方法有很多不同的版本,提供了数据写入buffer的不同方式。例如,在指定的位置写入,或者写入字节数组。更多关于缓冲区的细节可以参考JavaDoc。

flip()

flip()方法将Buffer从写模式转换到读模式。调用flip()会将position重置为0,将limit设置为刚才的position。
换句话说,position现在标记了读的位置,limit标记了有多少字节,字符等被写入到了buffer——也就是有多少字节,字符可以被读取。

从Buffer中读

有两种方法从Buffer中读取数据:

  1. 从缓冲区读取数据到通道。
  2. 用get()方法从缓冲区中读取数据
    例,从缓冲区中读到通道
// read from buffer into channel
int bytesWritten = inChannel.write(buf);

例,用get()方法从Buffer中读:

byte aByte = buf.get();

get()方法有很多版本,提供了从Buffer读取数据的不同方式。例如,从指定的位置读取,或者读取字节数据。更多关于缓冲区的细节可以参考JavaDoc。

rewind()

Buffer.rewind()方法将position重置为0。这样能够重读buffer中数据。limit保持不变,仍然表示能够从buffer中读取的数据量。

clear()和compact()

一旦读取完Buffer中的数据,需要让Buffer做好再次写入的准备。
可以通过调用clear()或者compact()方法实现。
clear()方法会将position重置为0,limit重置为capacity。也就是说,Buffer被清空了。Buffer中的数据并未清除,只有这些标记代表Buffer能被写入数据的位置。
如果Buffer中仍有未被读取的数据,clear()方法将导致它们被遗忘,这意味着不再有任何标记表明那些数据被读取了,哪些没有。
如果Buffer中仍然有未被读取的数据,且后续还需要读取它们,但此时需要先写入数据。就需要使用compact()方法代替clear()方法。
compact()将未读取的数据复制到Buffer 的开头,然后将position设置为最后一个未读元素的后面,limit属性和clear()方法一样,被设置为capacity。之后就可以往Buffer中写入数据了,并且不会覆盖未读数据。

mark()和reset()

可以通过调用Buffer.mark()方法在Buffer中标记一个指定的位置。之后可以通过Buffer.reset()方法恢复到这个位置。
例如:

buffer.mark();
// call buffer.get() a couple of time, e.g. during parsing.
buffer.reset;// set position back to mark

equals() 和compareTo()

使用equals()和compareTo()方法可以比较两个缓冲区。

equals()

满足一下条件,判断两个缓冲区相等:

  • 类型相同(byte,char,int等)
  • 剩余字节,字符等数量相同,
  • 所有剩余的字节,字符相同
    尅建,equals仅仅比较了Buffer中的部分数据,而不是其中的每一个元素,实际上,仅仅比较了Buffer中的剩余数据。

compareTo()

compareTo()方法比较了两个缓冲区中的剩余数据,在例如排序等需求中。如果满足一下条件,一个Buffer被认为小于另一个:

  1. 第一个不相等的元素小于另一个buffer中的对应元素(原文有误)。
  2. 所以的元素相等,但是第一个Buffer比另一个先耗尽(第一个Buffer的元素更少)。

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