分类
- HeapByteBuffer:基于 JVM 堆内存的字节缓冲区。
- DirectByteBuffer:基于系统直接内存的字节缓冲区,内部通过 Unsafe 类实现操作系统直接内存。
属性
- position:当前位置
- limit:操作边界
- capacity:容量
- mark:标记此时的 position
创建
默认将 pos 指向0,cap 与 lim 指向容器尾部。
与 allocate 相似,同时会初始化缓冲区内的数据,且不会因为初始数据而导致指针发生变化。
写入
每次写入会使 pos 指向下一个可操作位。
读取
ByteBuffer buf = ByteBuffer.allocate(5);
buf.put(new byte[] {'A', 'B', 'C', 'D', 'E'});
buf.flip(); // flip 方法后面会详细说到
1. array():返回缓冲区中的字节数组
byte[] bufArr = buf.array();
System.out.println(Arrays.toString(bufArr)); // [65, 66, 67, 68, 69]
2. get() / get(i)
get(i) 方法会直接从缓冲区中的字节数组取值。
get() 方法稍有差异,会将当前的 pos 作为索引到字节数组中取值,并更新 pos 使其自增一指向下一位。
3. 获取基础属性
buf.position();
buf.capacity();
buf.limit();
4. 获取剩余
buf.remaining();
public final int remaining() {
int rem = limit - position;
return rem > 0 ? rem : 0;
}
buf.hasRemaining();
public final boolean hasRemaining() {
return position < limit;
}
通过 hasRemaining() 配合 get() 即可迭代整个缓冲区
while (buf.hasRemaining()) {
System.out.print((char) buf.get() + "\t");
}
重置
其他很多博客都提出了读、写的概念,来操作缓冲区。不过个人觉得,不如直接理解方法的实际操作,了解其实现。不仅记忆的更深,使用时心里也清楚到底更改了哪些属性。也会明白,为什么不调用某些方法就操作不到容器数据。反之为什么调用了某些方法,就可以操作容器数据。
1. flip
重置指针,准备操作缓冲区当前 [0, pos] 区间。进行读取或者写入,取决于调用者。
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
例子,迭代缓冲区的内容:
ByteBuffer buf = ByteBuffer.allocate(5);
buf.put(new byte[]{'A', 'B', 'C', 'D', 'E'});
System.out.println(buf); // java.nio.HeapByteBuffer[pos=5 lim=5 cap=5]
// buf.flip();
while (buf.hasRemaining()) {
System.out.print((char) buf.get() + "\t");
}
不会打印任何容器数据,因为 put 方法会变更 pos 的位置。当添加了五个字符后,缓冲区的各个指针:[pos=5 lim=5 cap=5]
。那么当调用 hasRemaining 时,会检测 pos 已经处于 limit 位并返回 false。若强制直接调用 get ,也会抛出溢出异常。
调用 flip 方法重置各个指针,表示我要操作 [0, 此时的 pos] 区间数据,而此时的 pos 也由于 put 的原因指向了最后。这样就可以正常迭代了。
2. rewind
对比 flip,没有将 limit 设置为当前的 pos。
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
3. clear
清除当前的指针指向,重置为初始状态。仅是调整指针,不会真正清除缓冲区中的数据。
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
例子:
ByteBuffer buf = ByteBuffer.wrap(new byte[]{'A', 'B', 'C', 'D', 'E'});
// 迭代容器
while (buf.hasRemaining()) {
System.out.print((char) buf.get() + "\t");
}
System.out.println();
// clear
buf.clear();
System.out.println((char) buf.get()); // A
4. compact
丢弃已经操作过的数据,将还没有操作的数据压缩到容器首部,并将 pos 的指针指向仍为操作数据的下一位。
public ByteBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this;
}
示意图: