NIO——Buffer
作者:互联网
Buffer是Java NIO技术中的一个组件,简单来说它就是一个缓冲区,但它比我们自己编写的byte[] buf
具有更多功能,甚至还支持堆外的直接内存分配,比较适用于大型数据的传输。
本笔记只记录Buffer中我觉得不太熟悉的API。
mark
调用mark
时,当前Buffer的位置被记下,等调用reset
时,恢复之前标记的位置。
@Test
void testMarkAndReset() {
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put(new byte[] {1, 2, 3, 4}); // position = 4, limit = 10, cap = 10
System.out.println(buf);
buf.mark(); // mark = 4
buf.put(new byte[] {5, 6, 7, 8}); // position = 8, limit = 10, cap = 10
System.out.println(buf);
buf.reset(); // position = 4, limit = 10, cap = 10
System.out.println(buf);
}
mark
未被设置时,它是-1
,这时你调用reset
会出错,因为Buffer类认为你还没设置过mark
。
当position
或limit
小于mark
时,mark
会被丢弃,变成-1
。
直接缓冲
非直接缓冲是在堆内存中分配,得到的Buffer实例是java.nio.HeapByteBuffer
,这时你是向JVM申请缓冲区的空间,然后JVM再向操作系统申请,而直接缓冲越过JVM直接向操作系统申请,得到的是java.nio.DirectByteBuffer
实例。
DirectByteBuffer
类中使用Unsafe.allocateMemory
来进行直接内存分配。
可以使用ByteBuffer.isDirect
来判断该缓冲区是否来自直接内存,
@Test
void testDirectBuffer() {
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
Assertions.assertTrue(buf.isDirect());
}
hasArray
hasArray
返回Buffer是否具有底层数组。
public final boolean hasArray() {
return (hb != null) && !isReadOnly;
}
ByteBuffer类中维护了一个数组hb
,默认为null
,HeapByteBuffer
通过JVM堆来维护缓冲区,所以这个底层数组是它实现缓冲区的唯一方式,所以它初始化了这个数组并使用了。而DirectByteBuffer
通过直接内存来维护缓冲区,所以它不用也不能使用这个底层数组,所以它没有初始化这个数组。所以DirectByteBuffer
总是不具有底层数组的,它的hasArray
总返回false
。
此外,当Buffer是只读时,也返回false,只读Buffer一般是某个Buffer的一个包装,它们共享底层数组,但只读Buffer不能对底层数组进行写入。
@Test
void testHasArray() {
Assertions.assertTrue(
ByteBuffer.allocate(10).hasArray()
);
Assertions.assertFalse(
ByteBuffer.allocateDirect(10).hasArray()
);
Assertions.assertFalse(
ByteBuffer.allocate(10).asReadOnlyBuffer().hasArray()
);
}
rewind
清除标记,设置position为0,limit不变。当重新读取或写入一个缓冲区时使用。
读写
Buffer类提供了一系列的读写方法,它们以get/put
开头。简单把它们分成两类,绝对读写和相对读写。
绝对读写要求提供一个下标,它只是对下标位置的数据进行更改,Buffer的position并不会因此改变。
@Test
void testAbsolutePutAndGet() {
ByteBuffer buf = ByteBuffer.allocate(10);
// 通过buf.put(index, data) 在buf下标为5的地方写入10
buf.put(5, (byte) 10);
Assertions.assertEquals(0, buf.position());
Assertions.assertEquals(10, buf.get(5));
}
相对读写不需要提供下标,它从当前position位置开始写入,写入完成后更新position位置。
@Test
void testRelativePutAndGet() {
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put(new byte[] {1, 2, 3, 4, 5});
Assertions.assertEquals(5, buf.position());
}
wrap方法需要注意的一个点
wrap
方法直接使用一个传入的数组做为底层数组,它的容量等于传入数组大小。
容易迷惑的是wrap(byte[] buf, int offset, int length)
,看起来好像是创建了一个新的子数组作为底层数组,但不是,offset
会被设置成buffer的position,length + position
会被设置成buffer的limit。
@Test
void testWrap() {
// 缓冲区数组是{1, 2, 3, 4, 5, 6, 7, 8},其可用内容是{5, 6, 7}
ByteBuffer buf = ByteBuffer.wrap(new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8}, 4, 3);
Assertions.assertEquals(8, buf.capacity());
Assertions.assertEquals(4, buf.position());
Assertions.assertEquals(7, buf.limit());
Assertions.assertEquals(5, buf.get());
Assertions.assertEquals(6, buf.get());
Assertions.assertEquals(7, buf.get());
}
slice和arrayOffset
slice
根据当前ByteBuffer创建一个新的ByteBuffer。
达到的目的和Python中的切片、JS中的slice一样,都是获得一个Buffer的子Buffer,JavaNIO中的实现是让它们共用相同的底层数组,然后调节新Buffer的偏移量,也就是arrayOffset
。
@Test
void testSliceAndArrayOffset() {
ByteBuffer buf = ByteBuffer.wrap(new byte[] {1,2,3,4,5,6,7,8,9,0});
ByteBuffer buf2 = buf.slice(4, 3);
// buf和buf2共用同一个底层数组,只是buf2中的所有操作都需要加上一个为4的偏移量。
Assertions.assertEquals(4, buf2.arrayOffset());
}
新旧Buffer具有独立的position、mark、limit等信息,但是对于底层数组的修改会反映到所有共用该底层数组的Buffer上。
@Test
void testSameArray() {
ByteBuffer buf1 = ByteBuffer.wrap(new byte[] {1,2,3,4,5,6,7,8,9,0});
ByteBuffer buf2 = buf1.slice(4, 3);
buf2.put(0, (byte) 10); // buf2: 10, 6 ,7 ; buf1: 1, 2, 3, 4, 10, 6, 7, 8, 9, 0
byte[] arrayInBuf1 = new byte[buf1.remaining()];
buf1.get(arrayInBuf1);
Assertions.assertArrayEquals(
new byte[] {1,2,3,4,10,6,7,8,9,0},
arrayInBuf1
);
}
压缩缓冲区
compact
方法对缓冲区进行压缩。它的作用是将目前缓冲区中没读完的数据移动到缓冲区的最前面,并将指针放置到移动后的位置。
它的存在是为了应付防止循环中数据未写(读)完的情况
equals
不关心capacity,limit和position是否一致,只要两个缓冲区内的剩余内容(position到limit)完全一样即可。
- 两个都是ByteBuffer
- remining一样
- position到limit的每个字节都一样
下面的两个buffer的position、limit、capacity都不一致,但它们的剩余内容一致,所以它们相等。
@Test
void testEquals() {
ByteBuffer buf1 =
ByteBuffer.wrap(new byte[]{1, 2, 3, 100, 101, 102, 5, 4, 6});
buf1.position(3);
buf1.limit(6);
ByteBuffer buf2 =
ByteBuffer.wrap(new byte[]{100, 101, 102, 103, 104});
buf2.limit(3);
Assertions.assertTrue(buf1.equals(buf2));
}
同时compareTo
方法也是按照字典序比较它们的剩余序列。
标签:10,NIO,Buffer,Assertions,ByteBuffer,position,buf 来源: https://www.cnblogs.com/lilpig/p/16025391.html