四、Java NIO 缓冲区
所有文章
https://www.cnblogs.com/lay2017/p/12901123.html
正文
本文继续Java NIO的缓冲区(Buffer)部分。我们前面不断提及的,Buffer将在和Channel打交道的时候使用。我们从Channel读入数据到Buffer,或者从Buffer写入数据到Channel。
那么Buffer是什么呢?Buffer其实就是一块指定了一定容量大小的内存空间,你可以对Buffer进行数据的读写。你也可以简单理解为定义了一个数组,这个数组被封装到了一个Buffer对象当中,并且提供了一些列的操作方法,使得易于使用。
Buffer的基本用法
使用Buffer来读写数据大体要遵循以下4个步骤
1.将数据写入到Buffer当中
2.调用Buffer的flip方法,翻转position到0的位置
3.将从position=0的位置开始从Buffer读取数据
4.调用Buffer的clear方法或者Buffer的compact方法,将已经读取完的数据进行清理
当你向一个Buffer读入数据,Buffer会持续跟踪你写入了多少数据。一旦你想要读取数据的时候,你需要将Buffer从写入模式切换到读取模式,因此需要先调用flip方法。
一旦你读取完了所有的数据,你需要清理Buffer,为再次写入做准备。你可以调用clear或者compact方法。clear方法将清空整个Buffer。compact方法只是清理了你已经读取的那部分数据。未读取的那部分,将被移到Buffer的开始位置,数据的写入将从未读取数据的最后一个位置的下一个位置开始。
下面是一个Buffer的使用示例,包含了write、flip、read、clear操作
// 构建文件对象 RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); // 获取通道 FileChannel inChannel = aFile.getChannel(); //分配一个48字节的缓冲区 ByteBuffer buf = ByteBuffer.allocate(48); // 从通道读取数据到缓冲区 int bytesRead = inChannel.read(buf); // 当数据不为空 while (bytesRead != -1) { // 翻转position=0,进入读取模式,从0开始读取数据 buf.flip(); // 当Buffer还有剩余数据的时候 while(buf.hasRemaining()){ // 读取并打印数据 System.out.print((char) buf.get()); } // 清空整个Buffer,准备写入 buf.clear(); // 再次从通道读取数据到Buffer bytesRead = inChannel.read(buf); } // 关闭文件对象 aFile.close();
Buffer的capacity、position、limit
一个Buffer是一个内存空间,你可以对它进行数据的读写。这块内存空间封装成了一个Buffer对象,并提供一些方便的操作方法使得它易于使用。
Buffer主要有三个属性你必须知道的:
1.capacity:容量
2.position:位置
3.limit:限制
position和limit的值取决于当前是读取模式还是写入模式。capacity总是从头到尾保持不变,无论是读取还是写入模式。
下图是两种模式写,这三个值对应的示例
Capacity
作为一个内存空间,一个Buffer会有一个固定的大小,我们也叫做容量"capacity"。Buffer的最大空间是由capacity来确定的,如果你想要不断写入数据,你就需要不断clear调用Buffer中旧的数据,也就是为什么每次write到Buffer的时候要clear或者compact。
Position
当你想要对Buffer写入数据的时候,你是在一个确定的位置(position)上操作的,可以理解为数组的索引。在Buffer初始化的时候,position=0.当数据被写入到Buffer以后,position将会不断变化,指向下一个位置,不过position总是小于等于capacity-1
当你想要从Buffer读取数据的时候,也一样是从position的位置开始读取的,所以你需要调用flip将position=0。
Limit
在写入模式中,limit表示当前你可以写入Buffer的最大空间。limit总是等于capacity。
当你在读取模式的时候,调用了flip之后。在设置position=0之前会先把limit=position。这意味着你可以从position=0开始读取数据,直到limit前的数据。也就是说,之前写入了多少数据,这里就读取多少数据。
Buffer的类型
Java NIO的Buffer主要有以下几种类型
1.ByteBuffer
2.MappedByteBuffer
3.CharBuffer
4.DoubleBuffer
5.FloatBuffer
6.IntBuffer
7.LongBuffer
8.ShortBuffer
如你所见,以上的Buffer基本上就是对应了各种基本数据类型。MappedByteBuffer特殊一点,后续文章将会单独说明
创建一个Buffer
获取一个Buffer对象,其实就是分配一块内存空间。每一个Buffer实现类会有一个allocate方法来创建Buffer对象。
ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);
写入数据到Buffer
写入数据到Buffer中有两种方式
1.从Channel写入数据到Buffer
int bytesRead = inChannel.read(buf);
2.调用put方法自己写入数据到Buffer
buf.put(127);
flip
flip方法将Buffer从write模式切换到了read模式。调用flip方法position将设置为0,limit将设置为之前position所在的位置。
换句话说,position指定了从哪里开始读取数据,limit指定了你可以读取多少数据。
从Buffer读取数据
同样有两种方式从Buffer读取数据
1.从Buffer写入到Channel中
int bytesWritten = inChannel.write(buf);
2.从Buffer读取数据,通过get方法
byte aByte = buf.get();
rewind
rewind方法和flip方法类似,都是将position设置为0。但是rewind方法不影响limit的值,因此,如果你flip后读取了一次,想再读取一次。你可以使用rewind设置为0,重新读取。
clear和compact
一旦你从Buffer读取完了数据,你需要让Buffer可以再次写入。这时候你要调用clear或者compact方法。
如果你调用clear方法,position设置为0,limit设置为capacity。换句话说,Buffer是cleared状态,但是Buffer中的数据并没有清空。同时要注意,clear方法不管你是不是读取了Buffer中的所有数据,直接设置position=0,因此未读完的数据将被忽略。
如果你不想未读取完的数据被忽略,那么你需要调用compact方法,而不是clear方法。
compact方法会把未读取的数据拷贝到Buffer开始的位置,并设置position等于未读取完的数据的位置的下一个位置。并把limit=capacity,像clear一样。
调用了clear或者compact以后,Buffer将可以开始写入,Buffer中的数据将被覆盖。
mark和reset
如果你想标记position所在的位置,你需要调用mark方法。想要恢复position的位置到mark的位置,调用reset方法
buffer.mark(); buffer.reset(); //set position back to mark.
equals和compareTo
你可能想要比较两个Buffer
equals比较相等
如果满足以下条件则相等
1.数据类型相等
2.剩余数据量相等
3.剩余数据相等
compareTo比较大小
如果满足以下条件表示小于
1.第一个元素的大小更小
2.元素更少