引例
以一个例子 结合图形说明下 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方法