java.nio.Buffer类图文解析
来源:互联网 发布:阿里云 1m带宽 编辑:程序博客网 时间:2024/06/05 19:06
首先导读:ByteBuffer常用方法详解,我是从该文章开始了解,作图加深理解。
Buffer
ByteBuffer(以及其他类型缓冲区,如CharBuffer、IntBuffer等)继承自抽象类Buffer,用于在堆内存中预留一部分空间对IO数据做临时存储,以提高读写效率。各子类不同之处在于get/put数据的不同,而关于缓冲区读写区域的管理全部由父类Buffer负责。继承关系盗图如下:
4个属性
Buffer的3个关键成员变量:
final int capacity;int limit;int position = 0;
以上3个成员变量都有同名方法获取,以及1个关键的衍生值:
public final int remaining() { return limit - position; }
Buffer本质是一个数组,通过以上4个值管理这个工作数组的可读/写区域(我将其称之为“活动区域”),正确理解这些值是用好所有Buffer子类的关键。
结合示意图来理解这4个值:
【图1 Buffer示意图】
(假设,该Buffer处于读模式,即用户正在从该buffer读取数据)
- capacity:该buffer能容纳的数据总长度,即工作数组的长度,这里是16,数组元素下标为0~15。
- position:读取位置起点3,get()操作返回该位置的值”h”,并将该值+1。
- limit:可读取有效数据的界限,类似于数组的length。当前值为13,即get()操作最多读取到position=13-1=12的位置。
- remaining=limit - position:剩余可读取数据的数量,这里还有10个数据可以读取。
数值关系:position <= limit <= capacity
注意,当position == limit 时表示无数据可读取(或无位置可写入)。
为了理解这些属性,需要先理解一下Buffer的工作方式:
Buffer工作方式
对用户来说,对同一个buffer的使用分为两种操作:往buffer输入数据,之后从buffer读取数据。缓冲区的意义在于,读和写的节奏可能不一样,有可能写入10个数据却只读取了3个,这时新来数据需要写入,也应该可以接着之前的数据末尾继续写入,而不能把未读取的7个数据丢了。
Buffer的工作数组长度capacity在初始化时就确定并且不可改变,position与limit用于划分出数组的“活动区域”,remaining()计算得到的则是该区域的长度。对Buffer的读写操作实际上都是针对这块区域的操作:该“活动区域”在写模式下表示Buffer可以写入数据的位置范围;在读模式下表示Buffer中可以读取的有效数据的范围。
这里之所以强调有效,是由于Buffer始终使用同一个数组处理读和写,一个位置上的数据一旦被读取了,它就失效了,下次写入时这个位置就允许被写入新数据。Buffer处理失效数据,不是将数据移除,而是将position标志位进行变化。对于已读取的数据,只需要将其排除在“活动区域”之外,就不会被读取。如Buffer.clear()仅仅是将标志位position=0,limit=capacity,表示整个数组都可以被写入(覆盖)而已。
若对【图1】中的Buffer调用get方法,即buffer处于读模式,那么可读取下标为3~12之间的数据“helloworld”,其他区间的数据可能存在,但一定是无效的(如这里”hi!”意味着已被读取)。若对其调用put方法,即buffer处于写模式,那么将从位置3开始写入新数据,(这往往意味着”hi!”是已写入的数据)。
因此,要正确处理Buffer的读写操作,就要处理好各个标志位在读写模式切换时的变化。这个变化由两个方法负责:flip()、compact().
2个方法
flip()翻转
flip():(向前)翻转“活动区域”,准备开始读取
//Flips this buffer//该方法由抽象类Buffer定义public final Buffer flip() { limit = position; position = 0; return this;}
为何叫“翻转”?看图就理解了:
【图2:flip()示意图】
上图中绿色的“活动区域”经过flip()后,像翻书一样地向前(向左)翻转到了下图中绿色部分。将活动区域翻到开头后,就可以使用get()从头开始读取数据。因此,flip()一般用于准备读取buffer。
compact()压实
compact():(向前)压实“活动区域”,准备开始写入。
/** * Compacts this byte buffer. * The remaining bytes will be moved to the head of the * buffer, starting from position zero. Then the position is set to remaining(); the limit is set to capacity; ... *///该方法由Buffer的子类定义和实现public abstract ByteBuffer compact();
该方法由Buffer的子类定义和实现,从注释可知:将Buffer中剩余的数据区域移动到头部,再将“活动区域”设置为除数据区域外的末尾部分。
看图:
【图3:compact()示意图】
上图中绿色的“活动区域”经过compact()后,往前(往左)压实,挤掉了无效的0~2位置的三个数据;并重新将“活动区域”指向了尾部。压实后,就可以使用put()从绿色部分末尾,开始写入新数据。因此,compact()一般用于准备写入buffer。
ByteBuffer API总结
掌握4个值、2个方法后,Buffer类的使用参照api文档就可以了。
下面以ByteBuffer为例,总结下常用的几个方法,详细见开篇导读文章。
注意:各种get/put方法是否影响当前位置,需根据api说明使用。
注意点
- ByteBuffer.warp(byte[])以一个数组创建buffer后,该buffer默认处于读模式,即position=0,limit=capacity。此时若使用put进行写入,将从0位置开始覆盖掉初始化数组的数据。因此,若初始化的数据是有用数据,在写入开始前应使用compact()压实后,从尾部开始写入。
- 一定要注意get/put重载的方法是否影响当前位置,在读取、写入前都要用remaining()进行可读/写判断
- remaining()是动态计算得到,谨记在get/put后,该值可能发生改变。
- position==limit时,get/put越界。即position=limit-1是最后一个数据
测试结果
测试了几个api证实本文理解正确
getByteBuffer after wrap:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12,remaining=12ByteBuffer after get2:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=2,limit=12;remaining=10ByteBuffer after flip:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=2;remaining=2ByteBuffer after comp:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=2,limit=12;remaining=10flipByteBuffer after wrap:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12,remaining=12ByteBuffer after flip:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=0;remaining=0ByteBuffer after comp:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12;remaining=12putByteBuffer after wrap:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12,remaining=12ByteBuffer after put1:[ 72, 73, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=2,limit=12;remaining=10 ByteBuffer after flip:[ 72, 73, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=2;remaining=2ByteBuffer after comp:[ 72, 73, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=2,limit=12;remaining=10compact after wrapByteBuffer after wrap:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12,remaining=12ByteBuffer after comp:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=12,limit=12;remaining=0ByteBuffer after flip:[104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 33], position=0,limit=12;remaining=12
- java.nio.Buffer类图文解析
- JAVA NIO 详解Buffer类
- Java NIO 详解 Buffer类
- java nio 学习之 Buffer 类
- Java NIO(一) Buffer类源码分析
- java nio之Buffer
- java.nio.Buffer分析
- java.nio.Buffer分析
- Java NIO Buffer
- Java NIO(4-Buffer)
- Java NIO Buffer
- Java NIO 之 buffer
- Java NIO---buffer
- Java Nio Buffer
- Java NIO Buffer
- java NIO 入门 Buffer
- 图解Java NIO Buffer
- java nio(五)Buffer
- 【原创】【百度之星2017初赛A】1005 今夕何夕
- c++ primer 5 第6章 函数
- IntelliJ IDEA 配置阿里云
- hdu6108 2017"百度之星"初赛(A)1001小C的倍数问题(数学)
- BZOJ1085 SCOI2005 骑士精神
- java.nio.Buffer类图文解析
- 文章标题
- 线程的上锁和睡眠
- 关于在使用maven构建项目候,jar包错误的解决办法
- Java8 lambda表达式10个示例
- 算法学习之旅,初级篇(13)--素数判定
- 1018.锤子剪刀布 (20)
- java编程思想笔记-并发之DelayQueue和PriorityBlockingQueue
- 深入剖析 webpack 打包生成的一大堆代码到底是啥