ByteBuffer常用方法分析

引例

以一个例子 结合图形说明下 ByteBuffer中这几个方法的使用区别

public class ByteBufferDemo {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //这里存放Double 需要8个字节 如果ByteBuffer空间小于8个字节 报错BufferOverflowException
        byte[] bytes=new byte[]{'a','b','c'};
        buffer.put(bytes);
				//翻转byteBuffer 从写模式转为读模式
        buffer.flip();
        while(buffer.hasRemaining()){
   			 System.out.println(buffer.get());
				}
    }

输出结果

97
98
99

上面的案例中初始化了容量为10的bytebuffer

在这里插入图片描述

position=0, limit=capacity=10,写模式下,position 是写入位置,limit 等于容量

插入一个字节数组后

在这里插入图片描述

flip方法

ByteBuffer默认实现是HeapByteBuffer

public final Buffer flip() {
        limit = position;
        position = 0;
        //清除标记
        mark = -1;
        return this;
    }

调用flip方法后,

在这里插入图片描述

切换到读模式后,则从position->limit 开始读取数据,读完后

在这里插入图片描述

mark && reset方法

这两个方法主要用于为position打标记,并将position重置到打标记处

public final Buffer mark() {
        mark = position;
        return this;
    }

public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

举例演示 假如有需求 想从某position处 重新再读取一遍数据

public class ByteBufferDemo3 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        byte[] bytes=new byte[]{'a','b','c'};
        buffer.put(bytes);
        //翻转byteBuffer 从写模式转为读模式
        buffer.flip();
        System.out.println(buffer.get());
        //标记
        int position = buffer.position();
        System.out.println("在position="+position+"处打标记");
        buffer.mark();

        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }

        buffer.reset();

        System.out.println("再次从position="+position+"处读取数据");

        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }

    }
}

输出:

97
在position=1处打标记
98
99
再次从position=1处读取数据
98
99

一开始在插入字节数组后,缓冲区如下

在这里插入图片描述

调用过flip及get()方法后,缓冲区如下

在这里插入图片描述

此时 buffer.mark()在position=1处打下标记

在这里插入图片描述

再第一个从position=1遍历缓冲区后

在这里插入图片描述

调用buffer.reset()后缓冲区

在这里插入图片描述

因此可以再次从position=1读取一遍数据

clear方法
public class ByteBufferDemo {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        byte[] bytes=new byte[]{'a','b','c'};
        buffer.put(bytes);
        //翻转byteBuffer 从写模式转为读模式
        buffer.clear();
        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }
    }
}

输出结果:

97
98
99
0
0
0
0
0
0
0

通过观察发现,clear方法也可以实现翻转buffer,那它与flip有什么区别呢?

public final Buffer clear() {
        position = 0;
        limit = capacity;
        //清除标记
        mark = -1;
        return this;
    }

可以发现,它将缓冲区中各指针置为初始值了。

在这里插入图片描述

此时如果从position读到limit,则会读到很多空数据。读完后 缓冲区指针如下

在这里插入图片描述

所以如果想用clear方法 则尽量保证缓冲区数据是满的,不然会读出来无用数据,示例

public class ByteBufferDemo2 {
    public static void main(String[] args) {
        byte[] bytes=new byte[]{'a','b','c'};
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        buffer.put(bytes);
        //翻转byteBuffer 从写模式转为读模式
        buffer.clear();

        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }
    }
}

输出结果:

97
98
99

如果在clear方法后 再次插入了数据,则需要注意下,因为有可能position位置发生了变化,导致读取数据不完整。

public class ByteBufferDemo2 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        byte[] bytes=new byte[]{'a','b','c'};
        buffer.put(bytes);
        //翻转byteBuffer 从写模式转为读模式
        buffer.clear();
        buffer.put((byte) 100);
      //buffer.flip();
        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }
    }
}

输出结果:

98
99
0
0
0
0
0
0
0

在这里插入图片描述

clear()方法之后 调用put方法

在这里插入图片描述

因为put方法会移动position,因此读取不到刚插入的数据。如果不想移动position的话,可以尝试在put时指定index 这样就不会移动position了

注意:如果在上面的buffer.put((byte) 100);后调用flip方法,则会将position=0,limit=1 只能读取到刚才插入的字符

所以 在使用clear方法时 要权衡好取怎样的数据和怎么取数据。

compact方法

这个方法与前面有所不同,是把未读完的部分向前压缩,然后切换至写模式

public ByteBuffer compact() {
				//拷贝数据 将数据从position处开始拷贝,从hbindex=0开始接收数据 拷贝长度=limit-position
        System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
        //重新设置缓冲区position
        position(remaining());
        //重新设置limit限制
        limit(capacity());
        //清除mark标记
        discardMark();
        return this;
    }

示例

public class ByteBufferDemo {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        byte[] bytes=new byte[]{'a','b','c'};
        buffer.put(bytes);
        //翻转byteBuffer 从写模式转为读模式
        buffer.flip();
        buffer.get();

        buffer.compact();

        buffer.put((byte) 'd');
        buffer.put((byte) 'e');

        buffer.flip();

        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }


    }
}

输出结果:

98
99
100
101

调用了flip及get方法后,缓冲区如下

在这里插入图片描述

调用compact()方法后,缓冲区如下

在这里插入图片描述

因为前面调用了buffer.get() 所以原position=1, 并且原limit=3

此时position=limit-原position=3-1=2, limit=capacity=10

再次调用两次put方法后

在这里插入图片描述

后面的读取数据 就不赘述了。

总结

别忘记读写切换时 调用flip方法

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值