- 随机访问索引
ByteBuf的所有是从0开始的,第一个字节的索引是0,最后一个字节的索引是capacity()-1
/*** 随机访问索引*/public static void demo1() {ByteBuf byteBuf = Unpooled.buffer();for (int i = 0; i < byteBuf.capacity(); i++) {System.out.println((char) byteBuf.getByte(i));}}
- 顺序访问索引
ByteBuf中通过两个读写的索引将数据划分成三个区域空间
| 可丢弃字节(已被读取) | 可读字节(未被读取) | 可写字节 |
|---|---|---|
从0->readerIndex->writeIndex->capaticy的几个界值得顺序来划分。
- 可丢弃字节区域
通过调用discardReadBytes()可以丢弃它们并回收空间,这个区域的初始大小为0,随着readerIndex的增加而增加,将可丢弃区域丢弃后的空闲空间将会增加到可写区域,即减小writeIndex的值。
如果频繁的调用discardReadBytes使其可写最大化,可能会导致内存复制,因为可读字节必须被移动到缓冲区的开始位置,即readerIndex归0。
- 可读字节区域
可读字节区域存储了实际数据,包括新分配的、包装的或者复制的缓冲区的,默认的readerIndex为0。任何名称以read或者skip开头的操作都将检索或者跳过位于当前readerIndex的数据,将增加已读字节数(已读的数据会被划分为可丢弃字节区域)。
如果被调用的方法需要一个ByteBuf实例参数作为写入的目标,并且没有指定目标索引参数,那么该缓冲区的writeIndex也会被增加,例如:readBytes(ByteBuf dest),dest目标会触发write操作。
5. 可写字节区域
可写字节区域是指一个拥有未定义内容的、写入就绪的内存区域,新分配的缓冲区的writeIndex默认为0,任何以write开头的操作都将从当前的writeIndex处开始写数据,将增加已写字节数(写入后即可读数据)。如果写操作的目标也是ByteBuf实例,并且没有指定源索引的值,则源缓冲区的readerIndex也同样的会增加。
读和写超过目标的容量时都将触发数组越界的异常。
- 索引管理
- 读写索引标记与重置:在JDK的ByteBuffer中是通过mark(int readlimit)和reset()来进行标记和重置到指定位置的,在ByteBuf中同样,可以通过调用markReaderIndex()、markWriteIndex()、resetWriteIndex()、resetReaderIndex()来标记和重置。原理类似,只是没有readlimit参数指定标记什么时候失效。
- 将索引移动到指定位置:readerIndex(int)、writeIndex(int)
- 将索引归0:clear(),将索引设置为0,但是并不会清除缓冲区中的内容。clear()之后也也就意味着所有空间都变为可写的了,调用该方法比discardReadBytes()轻量得多,因为只重置索引不会复制任何的内存。
- 缓冲区的字节查找操作
用来确定值得索引的方法,最简单的是使用indexOf()方法,复杂的朝招可以通过需要一个ByteBufProcessor实例作为参数的方法达成,该方法只定了一个方法:
process(byte value),它将检查输入值是否是正在查找的值,且ByteBufProcessor提供了一些便利实例方法。
- 派生缓冲区(得到一个新的ByteBuf实例)
ByteBuf实例提供的派生方法:
- duplicate()
- slice()
- slice(int,int)
- Unpooled.unmodifiableBuffer(..)
- order(ByteOrder)
- readSlice(int)
以上的方法都将返回一个新的ByteBuf实例,且新实例拥有自己的读写索引和标记索引,内部存储和JDK的ByteBuffer一样也是共享的。意味着两个实例的数据只要有一方做过更改,另外的实例也会跟着修改。
ByteBuf的复制和切片
复制:如果需要一个现有缓冲区的真实副本,需要使用copy()、copy(int,int),不同于派生缓冲区,由这个调用返回的实例拥有独立的数据副本。
切片:如slice(int,index)方法拿到一个源实例的部分字节得到一个新的ByteBuf实例,但是它们共享同一个数据副本。
他俩之间最主要一个区别就在于有没有一个独立的数据副本,使用场景是一致的,而使用splice()方法可在场景下避免复制内存的开销。
