一 nio中的bytebuffer的认识
1.bytebuffer的数据结构
对于ByteBuffer,其主要有五个属性:mark,position,limit,capacity和array。这五个属性的作用如下:
mark:记录了当前所标记的索引下标;
position:对于写入模式,表示当前可写入数据的下标,对于读取模式,表示接下来可以读取的数据的下标;
limit:对于写入模式,表示当前可以写入的数组大小,默认为数组的最大长度,对于读取模式,表示当前最多可以读取的数据 的位置下标;
capacity:表示当前数组的容量大小;
array:保存了当前写入的数据。
这几个数据中,除了array是用于保存数据的以外,这里最终的主要是position,limit和capacity三个属性,因为对于写入和读取模式,这三个属性的表示的含义大不一样。
1.1写入模式:
如下图所示为初始状态和写入3个字节之后position,limit和capacity三个属性的状态:
从图中可以看出,在写入模式下,limit指向的始终是当前可最多写入的数组索引下标,position指向的则是下一个可以写入的数据的索引位置,而capacity则始终不会变化,即为数组大小。
注意:图片来源知乎,如果侵权,联系我删除
1.2读取模式
假设我们按照上述方式在初始长度为6的ByteBuffer中写入了三个字节的数据,此时我们将模式切换为读取模式,那么这里的position,limit和capacity则变为如下形式:
可以看到,当切换为读取模式之后,limit则指向了最后一个可读取数据的下一个位置,表示最多可读取的数据;position则指向了数组的初始位置,表示下一个可读取的数据的位置;capacity还是表示数组的最大容量。这里当我们一个一个读取数据的时候,position就会依次往下切换,当期与limit重合时,就表示当前ByteBuffer中已没有可读取的数据了。
注意:图片来源知乎,如果侵权,联系我删除
1.3下面用代码来说明相关api
flip:切换为读取模式
mark:这个属性是一个标识的作用,即记录当前position的位置,
在后续如果调用reset()或者flip()方法时,ByteBuffer的position就会被重置到mark所记录的位置。
因而对于写入模式,在mark()并reset()后,将会回到mark记录的可以写入数据的位置;
对于读取模式,在mark()并reset()后,将会回到mark记录的可以读取的数据的位置
代码打印运行结果:
ByteBuffer buffer = ByteBuffer.allocate(6);
System.out.println(buffer);
buffer.put((byte) 1);
buffer.put((byte) 2);
buffer.put((byte) 3);
buffer.mark();
buffer.put((byte) 4);
System.out.println("put后=======>" + buffer);
buffer.reset();
System.out.println("reset后=======>" + buffer);
buffer.flip();
System.out.println("flip后===>" + buffer);
buffer.get();
System.out.println("get后===>" + buffer);
buffer.mark();
System.out.println("再次mark后===>" + buffer);
buffer.reset();
System.out.println("再次reset后===>" + buffer);
运行结果如下:
java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]
put后=======>java.nio.HeapByteBuffer[pos=4 lim=6 cap=6]
reset后=======>java.nio.HeapByteBuffer[pos=3 lim=6 cap=6]
flip后===>java.nio.HeapByteBuffer[pos=0 lim=3 cap=6]
get后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]
再次mark后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]
再次reset后===>java.nio.HeapByteBuffer[pos=1 lim=3 cap=6]
二:netty中的bytebuf
1.数据结构
可以看出提供了两个pointer来操作buf。
2.相关api
因api太多,我只是用demo测试一下,常用的api。如果想看更详细的请移步官网https://netty.io/3.10/api/index.html
ByteBuf buf = Unpooled.buffer();
byte[] bytes = "zpf wants to be a good coder".getBytes();
buf.writeBytes(bytes);
System.out.println("写入byte数组后的buf==>"+buf);
int readCount = buf.readableBytes();
System.out.println("可读的长度为==>"+readCount);
byte[] readableBytes = new byte[readCount];
buf.readBytes(readableBytes);
System.out.println("调用buf.read之后的byteBuf==>"+buf);
ByteBuf clear = buf.clear();
System.out.println("clear后==>"+buf);
System.out.println("clear后的clear==>"+clear);
ByteBuf discardReadBytes = buf.discardReadBytes();
System.out.println("调用discardReadBytes后的buf==>"+buf);
System.out.println("discardReadBytes的值==>"+discardReadBytes);
String s = new String(readableBytes,"UTF-8");
System.out.println("再转回来String==>"+s);
运行结果如下:
写入byte数组后的buf==>UnpooledHeapByteBuf(ridx: 0, widx: 28, cap: 256)
可读的长度为==>28
调用buf.read之后的byteBuf==>UnpooledHeapByteBuf(ridx: 28, widx: 28, cap: 256)
clear后==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
clear后的clear==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
调用discardReadBytes后的buf==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
discardReadBytes的值==>UnpooledHeapByteBuf(ridx: 0, widx: 0, cap: 256)
再转回来String==>zpf wants to be a good coder
三:nio中的byteBuffer和netty中的bytebuf的区别
1.ByteBuffer必须自己长度固定,一旦分配完成,它的容量不能动态扩展;ByteBuf默认容器大小为256,支持动态扩容,在允许的最大扩容范围内(Integer.MAX_VALUE)。
2.ByteBuffer只有一个position标识位置的指针,读的时候需要手动的调用flip()和rewind()等,否则很容易导致程序处理失败。而ByteBuf有两个标识位置的指针,一个写writerIndex,一个读readerIndex,读写的时候不需要调用额外的方法。
3.NIO的SocketChannel进行网络读写时,操作的对象是JDK标准的java.nio.byteBuffer。由于Netty使用统一的ByteBuf替代JDK原生的java.nio.ByteBuffer,所以ByteBuf中定义了ByteBuffer nioBuffer()方法将ByteBuf转换成ByteBuffer。
嗯,应该还有差异,以上是我目前的了解,最近在看netty权威指南,如果有新的理解,会在及时记录一下。