ByteBuffer

ByteBuffer有两种类型,一种是

创建ByteBuffer

ByteBuffer b1 = ByteBuffer.allocate(size);

进入 ByteBuffer的allocate方法:

 public static ByteBuffer allocate(int capacity) {
        ...
        return new HeapByteBuffer(capacity,capacity);
    }

allocate()中根据参数创建了子类HeapByteBuffer对象,进入HeapByteBuffer(int,int):

HeapByteBuffer(int cap, int lim) {      
        super(-1, 0, lim, cap, new byte[cap], 0);
        /*
        hb = new byte[cap];
        offset = 0;
        */
    }

这里在JVM堆上创建了一个容量为cap的byte数组,并将数组和必要的参数传递给自己父亲ByteBuffer的构造函数 :

ByteBuffer(int mark, int pos, int lim, int cap,  
                 byte[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

ByteBuffer获取在子类HeapByteBuffer里创建的byte数组hb又调用了自己父亲Buffer的构造函数:

 Buffer(int mark, int pos, int lim, int cap) {       
        ...
        this.capacity = cap;
        limit(lim);
        position(pos);
        ...
        this.mark = mark;
        }
    }

Buffer初始化自己的capcity、limit和position,这几个参数都是HeapByteBuffer的构造方法传入的,mark值是-1,先来看limit()方法:

public final Buffer limit(int newLimit) {
        ...
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

limit赋值为newLimit,此时limitcapcity相等,然后对positionmark进行判断。
再来看一下position()方法:

public final Buffer position(int newPosition) {
        ...
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

position()只是将newPositon赋值给positionnewPosition是0,所以此时position是0。

此时ByteBuffer对象b1就创建并初始化完毕了,并得到:

position = 0;
offset = 0;
capcity = size;  // ByteBuffer 容量
limit = capcity;
mark = -1;  // 哨兵

init

操作

1-put数据

byte aByte = 23;
b1.put(aByte);

进入HeapByteBufferput(byte)函数:

public ByteBuffer put(byte x) {
        hb[ix(nextPutIndex())] = x;
        return this;
    }

hb是我们创建的byte数组,来看一下nextPutIndex():

final int nextPutIndex() {                          
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }

nextPutIndex()获取下一个插入数据的位置position,然后递增position。再来看一下ix()方法:

protected int ix(int i) {
        return i + offset;
    }

因为offset 初始化为0 , 所以ix(int)得到的值就是当前新数据要插入的位置positionput(byte)将数据插入到position位置后,position下一个插入数据的位置。

put1

假设我们使用put(byte)插入了6个数据(put(byte[],int,int)内部也是put(byte)):

put6

2-remaining获取有效数据的大小

put6x1

int iSize = b1.remaining();

来看一下remaining()方法:

public final int remaining() {
        return limit - position;
    }

由上图可知,此时remaining()返回的是ByteBuffer中剩余空间的大小,并不是有效数据的个数。所以我们需要先调用flip()方法:

b1.flip();

来看一下flip()方法:

 public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

flip()是当前ByteBuffer的状态改变如下:

flip

此时limitposition之间的数据即为有效数据。

int iSize = b1.remaining();  // 返回 6

3-get获取数据

byte tByte = b1.get();

看一下get()方法:

public byte get() {
        return hb[ix(nextGetIndex())];
    }

这里只有nextGetIndex()方法我们还不知道,来看一下:

final int nextGetIndex() {
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }

nextGetIndex()获取当前有效数据的位置position,然后使position指向下一个有效数据的位置,这里有一个position是否越界的检查position >= limit,因为有效数据不能超过limit

这样看来get()只是获取byte数组hb小标为当前position处的元素,并使position指向下一个有效数据位置。

此时ByteBuffer的状态图:

get1

同时ByteBuffer还提供了hasRemaining()方法,用来判断当前position是否在有效数据范围内:

public final boolean hasRemaining() {
        return position < limit;
    }

有了这个方法我们就可以很方便的使用循环提取ByteBuffer里的数据了:

        byte loopByte;
        while(b1.hasRemaining()){
            loopByte = b1.get();
        }

注意:由hasRemaining()remaining()的源码可知, 它们的返回结果意思取决于limit是否被position重置:
1. 未调用执行limit = position此类的方法,则有 : limit == capacityremaining()表示ByteBuffer的剩余容量,hasRemaining()表示判断ByteBufferposition是否超过capacity
2. 调用执行limit = position此类的方法,则有:limit = old_positionremaining()表示ByteBuffer的剩余有效数据的数量,hasRemaining()表示判断ByteBufferposition是否超过limit,也就是有效数据是否已经被取完。

4-rewind再次从头读取

加入我们使用get()读取了4个数据后:

get4

或者将所有有效数据都读完了:

getall

突然有业务要求我们再从头读取该怎么办? 这时可以使用rewind()方法:

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

可以看到rewind()position重置为0,这样我们就可以从头开始读取有效数据。

rewind

当然你也可以使用 position(int)方法为position设置新值,从有效数据的任意位置进行读取:

 public final Buffer position(int newPosition) {
        ...
        position = newPosition;
        ...
        return this;
    }

position设置新值,如:

b1.position(0);

使用position(int newPosition)时一定要满足:
newPosition < limit ,否则读取的将不是有效数据或出错。

5-clear清空数据

b1.clear();

看一下clear() 方法:

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

从源码可以看出,clear() 只是将position、limit、mark重置到刚创建时的状态,并没有对ByteBuffer的容量capacity和内部数据做任何操作。如下图:

clear
新插入的数据将直接覆盖原原有数据,就像他们不存在一样。

6-mark哨兵

当使用get(*)或put(*)获取和插入一些数据后突然又要求回到get(*)或put(*)之前时的position位置该怎么办?

除了自己手动通过保存position()返回的数据:

public final int position() {
        return position;
    }

再通过position(int)设置保存的值之外:

 public final Buffer position(int newPosition) {
        ...
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

ByteBuffer本身也为我们提供了有些方法:

        b1.mark();
        /**
         *  get()/put()操作后
         */
        b1.reset();

先来看一下mark()方法:

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

它使用mark记录了当前的position

再来看看reset()方法:

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

它将mark的值再赋值给position,这样position就回到了之前的位置。

注意: 有多种方法会改变mark的值,如:clear()rewind()等,确保在mark()reset()之间不要调用此类方法。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值