ByteBuffer
ByteBuffer类是在Java NIO中常常使用的一个缓冲区类,如果理解透彻的话使用它可以进行高效的IO操作,反之,结果可能让你有意想不到的“惊喜”!
ByteBuffer的扩展
HeapByteBuffer 和 DirectByteBuffer
- | 描述 | 优点 |
---|---|---|
HeapByteBuffer | 在jvm堆上面的一个buffer,底层的本质是一个数组 | 由于内容维护在jvm里,所以把内容写进buffer里速度会快些;并且,可以更容易回收 |
DirectByteBuffer | 底层的数据其实是维护在操作系统的内存中,而不是jvm里,DirectByteBuffer里维护了一个引用address指向了数据,从而操作数据 | 跟外设(IO设备)打交道时会快很多,因为外设读取jvm堆里的数据时,不是直接读取的,而是把jvm里的数据读到一个内存块里,再在这个块里读取的,如果使用DirectByteBuffer,则可以省去这一步,实现zero copy |
写模式下,往buffer里写一个字节,并把postion移动一位。写模式下,一般limit与capacity相等。
flip
写完数据,需要开始读的时候,将postion复位到0,并将limit设为当前postion。
get
从buffer里读一个字节,并把postion移动一位。上限是limit,即写入数据的最后位置。
clear
将position置为0,并不清除buffer内容。
mark & reset
mark相关的方法主要是mark()(标记)和reset()(回到标记).
JDK代码实现
JDK中flip方法的代码如下:
public final Buffer flip() { limit = position; position = 0; mark = -1; return this;}
在调用four.write(buff)
时,就将buff缓冲区中的数据写入到输出管道,此时调用ByteBuffer.clear()方法为下次从管道中读取数据做准备,但是调用clear方法并不将缓冲区的数据清空,而是设置position,mark,limit这三个变量的值,JDK中clear方法的代码如下:
public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this;}
这个方法命名给人的感觉就是将数据清空了,但是实际上却不是的,它并没有清空缓冲区中的数据,而至重置了对象中的三个索引值,如果不清空的话,假设此次该ByteBuffer中的数据是满的,下次读取的数据不足以填满缓冲区,那么就会存在上一次已经处理的的数据,所以在判断缓冲区中是否还有可用数据时,使用ByteBuffer.hasRemaining()方法,在JDK中,这个方法的代码如下:
public final boolean hasRemaining() { return position < limit;}
在该方法中,比较了position和limit的值,用以判断是否还有可用数据。
在ByteBuffer类中,还有个方法是compact,对于ByteBuffer,其子类HeapByteBuffer的compact方法实现是这样的:
public ByteBuffer compact() { System.arraycopy(hb, ix(position()), hb, ix(0), remaining()); position(remaining()); limit(capacity()); return this;}
如果position()方法返回当前缓冲区中的position值,remaining()方法返回limit与position这段区间的长度,JDK中的remaining()方法代码如下
public final int remaining() { return limit - position;}
所以compact()方法中第一条语句作用是将数组hb当前position所指向的位置开始复制长度为limit-position的数据到hb数组的开始出,其中使用到了ix()函数,这个函数是将参数值加上一个offset值,offset即一个偏移值,在这样的比如一个这样的场景对于一个很大的缓冲区,将其分成两段,第一段的起始位置是p1,长度是q1,第二段起始位置是p2,长度是q2,那么可以分别将这两段包装成一个HeapByteBuffer对象,然后这两个HeapByteBuffer对象(ByteBuffer的子类,默认实现)的offset属性分别设置为p1和p2,这样就可以通过在内部使用ix()函数来简化ByteBuffer对外提供的接口,在使用者看来,与默认的ByteBuffer并没有区别。
在compact函数中,接着将当前的缓冲区的position索引置为limit-position
,limit索引置为缓冲区的容量,这样调用compact方法中就可以将缓冲区的有效数据全部移到缓冲区的首部,而position指向下一个可写位置。
比如刚刚创建一个ByteBuffer对象buff时,position=0,limit=capacity,那么此时调用buff.hasRemaining()则会返回true
,这样来判断缓冲区中是否有数据是不行的,因为此时缓冲区中的存储的全部是0,但是调用一次compact()
方法就可以将position置为limit值,这样再通过buff.hasRemaining()就会返回false
,可以与后面的逻辑一起处理了。
ByteBuffer还有一个名为mark的方法,该方法设置mark索引为position的值,JDK中的代码如下:
public final Buffer mark() { mark = position; return this;}
与其功能相反的方法为reset方法,即将position的值设置为mark,JDK中的代码如下:
public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this;}
此外还有一个名为rewind的方法,这个方法将position索引置为0,mark索引置为-1,JDK中的代码如下:
public final Buffer rewind() { position = 0; mark = -1; return this;}
通过这些方法,就可以很方便的操作一个缓冲区,关键是要理解这些方法具体的作用,以及对三个索引值的影响(capacity是不变的)。
ByteBuffer继承自Buffer类,上面的方法四个索引值都定义在Buffer类中,操作索引值的方法也都定义在Buffer类中。
- 文章2300
- 用户1336
- 访客10860229
真正的成功是激励他人行动。
语法错误: 意外的令牌“标识符”
全面理解Gradle - 定义Task
Motrix全能下载工具 (支持 BT / 磁力链 / 百度网盘)
谷歌Pixel正在开始起飞?
获取ElementUI Table排序后的数据
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is
亲测!虚拟机VirtualBox安装MAC OS 10.12图文教程
华为手机app闪退重启界面清空log日志问题
android ndk开发之asm/page.h: not found
手机屏幕碎了怎么备份操作?
免ROOT实现模拟点击任意位置
新手必看修改DSDT教程
thinkpad t470p装黑苹果系统10.13.2