Java NIO Buffer(4)

  • Basic Buffer Usage
  • Buffer Capacity, Position and Limit
  • Capacity
  • Position
  • Limit
  • Buffer Types
  • Allocating a Buffer
  • Writing Data to a Buffer
  • flip()
  • Reading Data from a Buffer
  • rewind()
  • clear() and compact()
  • mark() and reset()
  • equals() and compareTo()
  • equals()
  • compareTo()

NIO buffers和 channels交互使用。正如你所知,数据总是从channel 读入buffer,从buffer写入channel。

buffer本质上是一个你可以先写入,然后读出数据的内存块,这个内存块用NIO buffer对象包装了起来以方便使用。

基本buffer类用法

使用Buffer类读写数据典型的套路写法有四步:

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

【译注: 读写buffer,是以buffer为主体的。写模式:实际是buffer.read() ,即将数据从channel读入buffer; 读模式,实际是buffer.write() ,即将数据从buffer读出写入channel】

写数据到buffer的时候,buffer会记录程序写入了多少数据。一旦开始读数据了,需要使用flip()从写模式切换到读模式。读模式允许你把所有已写的数据读出来。

一旦读完所有数据,需要clear(清理)buffer,以待再次写,NIO提供了两种方式:
clear()或者compact()。Clear()方法会“清理”掉buffer中所有内容;compact()仅仅会“清理“掉已读的内容,而所有未读内容会被移动到buffer内容器(数组)开始的一端,以后数据会被追加写到该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); //读出数据写入到buffer (写模式)
while (bytesRead != -1) {
  buf.flip();  //buffer正待被读出 (切换为 读模式 )
  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // 逐个字节读出
  }
  buf.clear(); //buffer正待被写入
  bytesRead = inChannel.read(buf);
}
aFile.close();

Buffer Capacity(容量), Position(位置) and Limit(上界)

Buffer 这个内存块有三个需要重点熟悉的属性:

  1. capacity
  2. position
  3. limit

position 、 limit 的含义取决于Buffer是读、还是写模式。capacity在这两种模式下倒是含义相同。
下图解释了这三个属性在读、写模式下的模型:
这里写图片描述

capacity

buffer作为内存块,有着固定的内存大小,也称“capacity”。程序写capacity这个容量的字节数据、长整型、字符数据到buffer里。buffer满了以后,如果想继续写入数据,需要首先清空它(将数据读出来,或者清理掉)

position

程序是在一个特定的位置(position)把数据写入buffer的。初始的position=0。写入一个byte、long数据到buffer后,position指针会向后移动一位。position指针最大=capacity-1.

从buffer读数据时也是从一个特定position开始的。当程序flip()一个buffer,将模式从写改为读时,position被重置为0。当你从buffer读取数据时,也会移动position指针到下一个要读的位置。

limit

在Buffer的写模式下,一个Buffer的limit指程序最多能往buffer中写入多少数据,此时limit= buffer的capacity。

在buffer的读模式下,limit 指能从buffer读取的最多数据。因此,flip一个buffer变成读模式后,limit 被置为 写模式下position的位置。换言之,程序写了多少数据最多就可以读多少数据(limit 被置为已写的字节数,这也被称为“Mark”)

Buffer的类型

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

Buffer有多种类型可供使用,MappedByteBuffer有点特殊,容后详解。

buffer对象内存分配(allocate)

实例化一个Buffer对象首先要使用allocate()方法为其分配内存。下面是一个容量为48个字节的ByteBuffer对象内存是如何分配的.

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);

这里是展示如何手动向Buffer写入数据的:

buf.put(127);    

put()有很多实现,写数据到Buffer的方式也各式各样。比如说,写入特定的position间数据,或者将一组字节写入到Buffer。可参考JavaDOC获取更多细节。

flip()

Flip()方法将一个Buffer 从写模式转为读模式。 调用flip()把position置为0,并把limit置为原position所在位置。
换言之:此时,position 标志了起读的位置(0),limit 则说明可以最多写多少字节、字符等数据到Buffer里。

从Buffer读取数据

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

  • 1、将Buffer中数据读到Channel
  • 2 、用get()获取buffer中数据

下面是如何将数据从Buffer读到Channel:

int bytesWritten = inChannel.write(buf);

这个是如何使用get()获取Buffer中数据:

byte aByte = buf.get();    

get()方法也有多种实现。比如,从特定position间读,或者从buffer中读取字节数组,等等。
可查看JavaDOC获取详情。

rewind()– (就是多次读buffer所需操作)

(英文原指:rewind-倒带,退卷)
Buffer .rewind()方法将position指针置为0,这样可以**重读**buffer中所有数据。limit 不变,仍然可以标志最多可以从Buffer中获取多少字节、字符等。

clear()、compact()

从Buffer中读完数据后,必须重设Buffer的状态才能写入数据。可以通过clear() compact()做到。

调用clear()方法,position指针置为0,limit = capacity。换言之:Buffer被“clear”了,但实际上Buffer中数据并没有被真正“clear”掉,真正被“clear”掉的是那些表明从哪里可以清除数据的标志(markers).

调用clear()时如果buffer里还有未读数据,这些数据将会被“遗忘”掉,这意味着将没有标志(markers)说明哪些数据已读、哪些未读。

如果Buffer里还有未读数据,而且你后续想读,只是现在需要写入一些数据的话,调用compact(),而不是clear()。

compact()会把未读数据copy到Buffer内存块(数组)。然后,把position置为未读内容的最后元素所在处。limit仍被置为capacity,就像clear()一样。这样,Buffer内就可继续写数据,但你不会覆盖掉未读的数据了。

Mark()、reset()

你可以通过Buffer.mark() “mark”一下 buffer中某个position。然后通过Buffer.reset() 把position重设到 mark的位置。示例:

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在满足下列条件下是equal的:

  1. 类型相同(byte 、char等)
  2. 两者剩余相同数量的bytes、chars 等
  3. 所有剩余的字节、字符都是equal的

    如你所见,equals()只比较Buffer的部分元素,而不是逐个比较buffer中的单个元素。事实上,本方法只比较Buffer中剩余元素。

CompareTo()
CompareTo() 比较两个buffer的 剩余元素(bytes 、chars等),比如在排序规则中会调用本方法。一般很少用到。

猜你喜欢

转载自blog.csdn.net/qq_30118563/article/details/80363773