Java NIO Buffer

Java NIO Buffer(缓冲区)通常与NIO Channels(通道)一起使用。如我们 前面 所提到的那样,数据从channel中读取到buffer中,也可以从buffer写入到channel 中。

Buffer 本质上是一个可以写入数据的内存块,并且之后你又可以从中再次读取数据。 此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块。

Buffer基本用法

使用Buffer去读取和写入数据时,通常要遵循以下4个步骤:

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

将数据写入缓冲区时,缓冲区会跟踪您写入的数据量。一旦你需要从中读取数据时,你需要调用 flip() 方法,将缓冲区从写入模式切换成读取模式。在读取模式下,你可以读取到所有写入到缓冲区的数据。

一旦你读取到了所有的数据,你需要对缓冲区做一次清理(clear),让其为下一次写入数据做好准备。你可以采用以下两种方式:一是调用 clear() 方法,而是调用 compact() 方法。clear() 方法是对整个缓冲区做清理。compcat() 方法只是清理掉你已经读取过的数据,任何未读取过的数据会被移动到缓冲区的起始位置,新写入进来的数据将会放到这些未读取的数据后面。

我们来看一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
RandomAccessFile fromFile = new RandomAccessFile("/Users/wangwei/Desktop/fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();

// create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

// read data from channel into buffer
int bytesRead = fromChannel.read(buf);
while (bytesRead != -1) {

// switch to read mode
buf.flip();

while (buf.hasRemaining()) {
System.out.println((char) buf.get());
}

// make buffer ready for writing
buf.clear();
bytesRead = fromChannel.read(buf);
}

fromChannel.close();
fromFile.close();

Buffer容量、位置与限制

Buffer 本质上是一个可以写入数据的内存块,并且之后你又可以从中再次读取数据。 此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块。

为了理解Buffer的工作原理,你需要去熟悉Buffer的三个属性:

  • capacity(容量)
  • position(位置)
  • limit(限制)

position与limit的具体含义取决于Buffer处于哪种模式,而capacity则与Buffer的模式无关。示意图如下:

JavaNIOBuffer

Capacity

作为一个内存块,缓冲区具有一定的固定大小,也称为“容量”。 你最多只能写入容量大小的字节(bytes)、长整型(longs)、字符(chars)等数据。一旦缓冲区满了,你需要清空它,才能继续写入数据。

Position

当你写入数据时,你需要在某个position处才能执行此操作。初始位置为0,当一个字节、长整数等已写入缓冲区时,position被提前指向缓冲区中的下一个单元以备插入新的数据。 Position的最大值为 capacity - 1.

当你从缓冲区读取数据时,也需要从给定位置开始读取数据。当您将缓冲区从写入模式切换到读取模式时,position将重置为0。当你从缓冲区读取数据时,将从position所指定的位置开始读取数据,并将position提前设置到下一个要读取的位置。

Limit

在写入模式下,Buffer的限制是您可以写入缓冲区的数据量的限制,limit就等于capacity。

将buffer切换为读取模式时,limit就变成了你可以从中读取到的最大数据量。 因此,当Buffer切换为读取模式时,limit被设置为写入模式的写入位置。 换句话说,就是你可以读取到的写入字节数。

Buffer类型

Java NIO有以下几种Buffer类型:

IO_NioBuffer

如您所见,这些Buffer类型分别代表了不同的数据类型。 换句话说,它们允许您使用char,short,int,long,float或double来处理缓冲区中的字节。

MappedByteBuffer有点特殊,以后单独介绍。

使用

Allocating a Buffer

直接调通 Buffer.allocate() 就可以获取一个Buffer对象,示例代码:

1
2
3
4
5
// 48 bytes
ByteBuffer buffer = ByteBuffer.allocate(48);

// 1024 characters
CharBuffer charBuffer = CharBuffer.allocate(1024);

写入数据

Buffer有两种写入数据的方式:

  1. 从Channel写入到Buffer

    1
    int bytesRead = inChannel.read(buf); //read into buffer.
  2. 直接调用 buffer.put() 方法

    1
    buf.put(127);

    有关put()的更多用法,具体参见JavaDoc文档.

flip()

前面说过,flip()用于切换Buffer的运行模式,并相应地改变position与limit的值。

buffer_lifecycle

读取数据

Buffer有两种方式可以读取数据:

  1. 从Buffer读取数据到Channel中。

    1
    2
    //read from buffer into channel.
    int bytesWritten = inChannel.write(buf);
  2. 直接调用get()方法获取数据。

    1
    byte aByte = buf.get();

    有关get()的更多用法,具体参见JavaDoc文档.

rewind()

Buffer.rewind()方法会将 position 充值为0,这样就可以重新读取所有的数据。但limit的值不受影响,因此这就导致会读取出很多空的元素数据。

clear() 和 compcat()

完成从缓冲区读取数据后,必须使缓冲区准备好再次写入。 您可以通过调用clear()或调用compact()来完成此操作。

如果你调用 clear() 方法,position会设置为0,limit会设置为capacity ,但是Buffer中的数据并没被真正删除掉。只有标记告诉您可以将数据写入缓冲区的位置。

如果在调用 clear() 时缓冲区中有任何未读数据,数据将被“遗忘”,这意味着您不再有任何标记告诉读取了哪些数据,以及未读取的数据。

如果缓冲区中仍有未读数据,并且你想稍后读取它,但你需要先进行一些写入,请调用 compact() 而不是clear()

compact() 将所有未读数据复制到Buffer的开头。然后它将位置设置在最后一个未读元素之后。 limit属性仍设置为capacity,就像 clear() 一样。现在缓冲区已准备好写入,但您不会覆盖未读数据。

mark() 和 reset()

你可以通过调用 Buffer.mark() 方法在Buffer中标记给定位置。 然后,你可以通过调用 Buffer.reset() 方法将位置重置回标记位置。 示例代码:

1
2
3
4
5
6
// 设置 mark 值等于 position
buffer.mark();

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

equals() 和 comareTo()

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

equals()

两个Buffer如果满足以下条件,则表示相等:

  • 类型一致
  • 剩余的字节、字符等数量相同
  • 剩余的字节、字符内容都相同

如您所见,equals仅比较缓冲区的一部分,而不是它内部的每个元素。 实际上,它只是比较缓冲区中的剩余元素。

compareTo()

compareTo() 方法比较两个缓冲区的剩余元素(字节,字符等)的大小。 在下列情况下,缓冲区被视为“小于”另一个缓冲区:

  • 与另一个缓冲区中的对应元素相等的第一个元素小于另一个缓冲区中的元素。
  • 所有元素都相等,但第一个缓冲区在第二个缓冲区之前耗尽了元素(它只有更少的元素)。

参考资料

请我喝杯咖啡吧~