Java NIO Buffer 详解 已翻译 100%

xiaoaiwhc1 投递于 09/05 10:54 (共 9 段, 翻译完成于 09-14)
阅读 774
收藏 25
2
加载中

在与NIO通道交互时使用Java NIO Buffer。 如您所知,数据从通道读入缓冲区,并从缓冲区写入通道。

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

基本缓冲区用法

使用缓冲区读取和写入数据通常遵循这4个小步骤:

  1. 写入数据到缓冲区

  2. 调用 buffer.flip()

  3. 从缓冲区读取数据

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

溪边九节
溪边九节
翻译于 09/12 09:22
0

当你将数据写入Buffer时,Buffer会跟踪你已经写入了多少数据。一旦你需要读出数据,你需要调用 flip() 方法将Buffer从写模式转换到读模式。在读模式,Buffer允许你将之前写入的数据全部读出。

一旦你已经读出了所有数据,你需要清除Buffer,为下次写入数据做准备。可以通过以下两种方法来完成:clear() 和 compact()。clear() 方法清除整个Buffer,而 compact() 方法仅仅清除你已经读出的Buffer,未读数据会被移动到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 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();

 

xiaoaiwhc1
xiaoaiwhc1
翻译于 09/12 10:51
0

Buffer的三个属性:容量,位置和限定符

Buffer本质上是一块你可以写入数据的内存区域,当然你也可以在写入之后读出数据。该内存区域被封装成一个 NIO Buffer 对象,它提供一系列的方法,以方便对该内存区域的操作。

为了学习Buffer 是如何工作的,Buffer 的三个属性你必须要熟悉,它们是:

  • capacity (容量)

  • position (游标位置)

  • limit (末尾限定符)

其中,position 和 limit 的意义依赖于当前 Buffer 是处于读模式还是写模式。capacity 的含义无论读写模式都是相同的。

下面是对以上三个属性在读模式和写模式的一个示例,后面会有详细的解释:

               Buffer capacity, position and limit in write and read mode.

xiaoaiwhc1
xiaoaiwhc1
翻译于 09/12 11:07
0

Capacity (容量)

作为一个内存块,Buffer 有一个固定的大小,我们叫做 “capacity(容量)"。你最多只能向 Buffer 写入 capacity 大小的字节,长整数,字符等。一旦 Buffer 满了,你必须在继续写入数据之前清空它(读出数据,或清除数据)。

Position (游标位置)

当你开始向 Buffer 写入数据时,你必须知道数据将要写入的位置。position 的初始值为 0。当一个字节或长整数等类似数据类型被写入 Buffer 后,position 就会指向下一个将要写入数据的位置(根据数据类型大小计算)。position 的最大值是 capacity - 1。

当你需要从 Buffer 读出数据时,你也需要知道将要从什么位置开始读数据。在你调用 flip 方法将 Buffer 从写模式转换为读模式时,position 被重新设置为 0。然后你从 position 指向的位置开始读取数据,接下来 position 指向下一个你要读取的位置。

xiaoaiwhc1
xiaoaiwhc1
翻译于 09/12 11:26
0

限制(Limit)

在写模式下对一个Buffer的限制即你能将多少数据写入Buffer中。在写模式下,限制等同于Buffer的容量(capacity)。

当切换Buffer为读模式时,限制表示你最多能读取到多少数据。因此,当切换Buffer为读模式时,限制会被设置为写模式下的position值。换句话说,你能读到之前写入的所有数据(限制被设置为已写的字节数,在写模式下就是position)。

Buffer类型

Java NIO提出了如下几种Buffer类型:

  • ByteBuffer

  • MappedByteBuffer

  • CharBuffer

  • DoubleBuffer

  • FloatBuffer

  • IntBuffer

  • LongBuffer

  • ShortBuffer

正如你所看到的,这些Buffer类型代表了不同的数据类型。换句话说,他们让你可以在使用的时候用char, short, int, long, float 或者double类型来代替直接使用buffer中的字节。

其中MappedByteBuffer有点特殊,将在它自己的部分来阐述。

琪花亿草
琪花亿草
翻译于 09/12 14:53
0

分配一个Buffer

若要获取一个Buffer对象,必须先分配它。每个Buffer类都有allocate()函数用来分配。下面的例子展示了分配一个ByteBuffer,其容量为48字节。

ByteBuffer buf = ByteBuffer.allocate(48);

下面的例子是分配一个具有1024个字符的空间的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

将数据写入Buffer

有两种方法可以将数据写入Buffer:

  1. 从Channel将数据写入Buffer

  2. 调用buffer的put()函数,自己将数据写入Buffer。

下面的例子是展示Channel如何将数据写入到Buffer中:

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

下面的例子是通过put()函数将数据写入Buffer:

buf.put(127);

put()函数还有很多其他版本,可以让你使用不用的方法将数据写入到Buffer。例如,在特定的位置写,或者将字节数组写入到buffer。查看JavaDoc来了解buffer实现的更多细节。

琪花亿草
琪花亿草
翻译于 09/12 15:04
0

flip()

用 flip() 方法将 Buffer f从写入模式切换到读取模式。调用 flip() 将 position 设置回0,并将 limit 置为刚才的位置。

换句话说,position 现在标记了读取位置,limit 标记了写入缓冲区的字节、字符数等——可以读取的字节数、字符数等的限制。

从缓冲区读取数据

有两种方法可以从 Buffer 中读取数据。

  1. 从缓冲区读取数据到通道。

  2. 使用缓冲区自带方法中的 get() 方法从缓冲区读取数据。

下面是如何将数据从缓冲区读取到通道的例子:

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);

下面是使用 get() 方法从 Buffer 中读取数据的例子:

byte aByte = buf.get();

get() 方法还有许多其他版本,允许您以多种不同的方式从 Buffer 中读取数据。 例如,在特定位置读取,或者从缓冲区读取字节数组。有关具体缓冲区实现的详细信息,请参阅JavaDoc。

kevinlinkai
kevinlinkai
翻译于 09/12 15:50
0

rewind()

Buffer.rewind() 将 position 设置回0,因此你可以重读缓冲区中的所有数据。这个 limit 保持不变,因此仍然标记有多少元素(字节、字符等)可以从Buffer读取。

clear() and compact()

从 Buffer 中读取数据之后,必须让 Buffer 为再次写入做好准备。您可以通过调用 clear() 或调用 compact()来做到这一点。

如果您调用 clear() ,position 将被设置为0,并 limit 置为 capacity。换句话说,Buffer 被清除。Buffer 中的数据没有清除。只有标记告诉您可以将数据写入 Buffer 的位置。

当您调用 clear() 时,如果缓冲区中有任何未读数据,则数据将被“遗忘”,这意味着您不再有任何标记来说明哪些数据已被读取,哪些数据未被读取。

如果 Buffer 中仍然有未读数据,并且您希望稍后读取它,但是您需要先写一些东西,那么调用 compact() 而不是 clear().

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

kevinlinkai
kevinlinkai
翻译于 09/12 15:35
0

mark() and reset()

你可以调用 Buffer.mark() 方法在 Buffer 中标记给定位置。之后,你可以调用 Buffer.reset() 方法重置回标记的这个位置。下面是个例子:

buffer.mark();

//call buffer.get() a couple of times, e.g. during parsing.

buffer.reset();  //set position back to mark.

equals() and compareTo()

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

equals()

两个缓冲相同,如果:

  1. 他们是同一个类型(byte,char,int等)。

  2. 在缓冲区,它们遗留有相同量的字节、字符等。

  3. 所有遗留的字节、字符都相同。

正如你看到的,equals只比较Buffer的一部分,而不是每个元素。事实上,它只比较Buffer中遗留的元素。

compareTo()

用 compareTo() 方法比较两个缓冲区的遗留元素(字节、字符等),用在例如排序例程。在下列情况中,一个缓冲区被视为“小于”另一个缓冲区,如果:

  1. 与另一个缓冲区对应元素相等的第一个元素,小于另一个缓冲区的元素。

  2. 所有的元素都相等,但是第一个缓冲区在第二个缓冲区之前耗尽了元素(它有更少的元素)。

kevinlinkai
kevinlinkai
翻译于 09/12 15:20
0
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(0)

返回顶部
顶部