zoukankan      html  css  js  c++  java
  • Java NIO中的缓冲区Buffer(一)缓冲区基础

    什么是缓冲区(Buffer)

    定义

    简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer、ByteBuffer、DoubleBuffer等的源码),Buffer类内部其实就是一个基本数据类型的数组,以及对这个缓冲数组的各种操作;

    常见的缓冲区如ByteBuffer、IntBuffer、DoubleBuffer...内部对应的数组依次是byte、int、double...

    与通道的关系

    在Java NIO中,缓冲区主要是跟通道(Channel)打交道,数据总是从缓冲区写入到通道中,或者从通道读取数据到缓冲区;

    继承结构

    关于Buffer的继承结构,我们可以简单的以ByteBuffer为例,如下:

    Buffer是顶层抽象类,ByteBuffer继承Buffer,也是抽象类,ByteBuffer最常见的两个具体实现类如下:

    DirectByteBuffer(JVM堆外部、通过unsafe.allocateMemory实现)、HeapByteBuffer(JVM堆

    缓冲区的四个属性(capacity、limit、position、mark)

    容量(capacity)

    capacity指的是缓冲区能够容纳元素的最大数量,这个值在缓冲区创建时被设定,而且不能够改变,如下,我们创建了一个最大容量为10的字节缓冲区;

    ByteBuffer bf = ByteBuffer.allocate(10);

    上界(limit)

    limit指的是缓冲区中第一个不能读写的元素的数组下标索引,也可以认为是缓冲区中实际元素的数量;

    位置(position)

    position指的是下一个要被读写的元素的数组下标索引,该值会随get()和put()的调用自动更新;

    标记(mark)

    一个备忘位置,调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值;

    四个属性值之间的关系

    根据以上四个属性的定义,我们可以总结出它们之间的关系如下:

    0 <= mark <= position <= limit <= capacity

    举个例子,观察四个属性值的变化

     1、创建一个容量大小为10的字符缓冲区

    ByteBuffer bf = ByteBuffer.allocate(10);

    此时:mark = -1; position = 0; limit = 10; capacity = 10;

    2、往缓冲区中put()五个字节

    bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'0');

    注意这里一个字符是占用两个字节的,但是英文字符只占用一个字节,所以这样是可以实现储存效果的;

    此时:mark = -1; position = 5; limit = 10; capacity = 10;

    3、调用flip()方法,切换为读就绪状态

    bf.flip();

    此时:mark = -1; position = 0; limit = 5; capacity = 10;

     

     4、读取两个元素

    System.out.println("" + (char) bf.get() + (char) bf.get());

     此时:mark = -1; position = 2; limit = 5; capacity = 10;

    5、标记此时的position位置

    bf.mark();

    此时:mark = 2; position = 2; limit = 5; capacity = 10;

    6、读取两个元素后,恢复到之前mark的位置处

    System.out.println("" + (char) bf.get() + (char) bf.get());
    bf.reset();

    属性变化情况:

    执行完第一行代码:mark = 2; position = 4; limit = 5; capacity = 10;

    执行完第二行代码:mark = 2; position = 2; limit = 5; capacity = 10;

    7、调用compact()方法,释放已读数据的空间,准备重新填充缓存区

    bf.compact();

    此时:mark = 2; position = 3; limit = 10; capacity = 10;

     注意观察数组中元素的变化,实际上进行了数组拷贝,抛弃了已读字节元素,保留了未读字节元素;

     缓冲区比较

    其实查看equals源码就可以知道是如何比较的,如下(以ByteBuffer为例):

    public boolean equals(Object ob) {
            if (this == ob)
                return true;
            if (!(ob instanceof ByteBuffer))
                return false;
            ByteBuffer that = (ByteBuffer)ob;
            if (this.remaining() != that.remaining())
                return false;
            int p = this.position();
            for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
                if (!equals(this.get(i), that.get(j)))
                    return false;
            return true;
        }

    总的来说,两个缓冲区被认为相等的条件如下(以下内容直接摘自《Java NIO》):

    1. 两个对象类型相同。包含不同数据类型的 buffer 永远不会相等,而且 buffer绝不会等于非 buffer 对象。
    2. 两个对象都剩余同样数量的元素。Buffer 的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从位置到上界)必须相同。
    3. 在每个缓冲区中应被 Get()方法返回的剩余数据元素序列必须一致。

    批量读写缓冲区数据

    以ByteBuffer为例,使用如下API即可:

    public ByteBuffer get(byte[] dst, int offset, int length)

    public ByteBuffer put(byte[] src, int offset, int length)

    public ByteBuffer get(byte[] dst)

    public final ByteBuffer put(byte[] src)

    实际上,后面两种方法内部就是调用前面两种方法的;

    参数的含义直接查看源码注释即可,写的很清楚,如put(byte[] src, int offset, int length)方法的注释:

        /* @param  src
         *         The array from which bytes are to be read
         *
         * @param  offset
         *         The offset within the array of the first byte to be read;
         *         must be non-negative and no larger than <tt>array.length</tt>
         *
         * @param  length
         *         The number of bytes to be read from the given array;
         *         must be non-negative and no larger than
         *         <tt>array.length - offset</tt>
         */

     参考资料

    《Java NIO》

  • 相关阅读:
    ActiveMq主从机制
    Calling handler.OnEndpointsUpdate报错原因
    【Android Studio安装部署系列】二十二、Android studio自动生成set、get方法
    【Android Studio安装部署系列】二十一、Android studio将项目上传到github中
    【Android Studio安装部署系列】二十、Android studio如何将so文件添加到svn中
    【Android Studio安装部署系列】十九、Android studio使用SVN
    【Android Studio安装部署系列】十八、Android studio更换APP应用图标
    【Android Studio安装部署系列】十七、Android studio引用第三方库、jar、so、arr文件
    【Android Studio安装部署系列】十六、Android studio在layout目录下新建子目录
    【Android Studio安装部署系列】十五、Android studio添加Assets目录
  • 原文地址:https://www.cnblogs.com/chenpi/p/6475510.html
Copyright © 2011-2022 走看看