一 点睛
NIO,可以称为 New IO 或 Non Blocking IO,是在 JDK 1.4 后提供的新 API。传统的I/O 是阻塞式的 I/O、面向流的操作;而 NIO 是非阻塞 I/O 、面向通道(Channel) 和 缓冲区(Buffer) 的操作。此外,NIO 还提供了选择器(Selector)全新的概念,这些都使 NIO 在传输数据时更加高效。下图为选择器、通道和缓冲区的关系图。
二 NIO 数据存储结构——缓冲区 Buffer
Buffer 的底层是一个数组,用于存储数据。NIO 提供了 7 种类型的缓冲区,用于存储不同类型的数据:ByteBuffer、IntBuffer、ShortBuffer、LongBuffer、FloatBuffer、DoubleBuffer、CharBuffer,并且它们都继承自 java.nio.Buffer。
在 Buffer 类中有 5 个重要的属性。
1 int position:下一个将要被读或写的元素位置。
2 int limit:限制 Buffer 中能够存放的元素个数,换句话说,limit 及之后的位置不能使用。
3 int capacity:Buffer 的最大容量,并且在创建后不能更改。
4 int mark:标记,可以在 Buffer 设置一个标记,之后可以通过 reset() 方法返回到列表该标记的位置。
5 long address:堆外内存的地址。
三 缓存区的常用方法
以 ByteBuffer 为例进行说明
方法 |
简介 |
public static ByteBuffer allocate(int capacity) |
分配大小为 capacity 的非直接缓冲区(单位byte) |
public static ByteBuffer allocateDirect(int capacity) |
分配大小为 capacity 的直接缓冲区(单位byte) |
public abstract ByteBuffer put(byte b) public abstract ByteBuffer put(int index, byte b) public final ByteBuffer put(byte[] src) public ByteBuffer put(byte[] src, int offset, int length) |
向缓冲区中存放数据 |
public abstract byte get() public abstract byte get(int index) public ByteBuffer get(byte[] dst) public ByteBuffer put(byte[] src, int offset, int length) |
从缓存区中读取数据 |
public abstract ByteBuffer asReadOnlyBuffer() |
将一个 Buffer 转为一个只读 Buffer。之后,不能再对转换后的 Buffer 进行写操作 |
public abstract ByteBuffer slice() |
将原 Buffer 从 position 到 limit 之间的部分数据交给一个新的 Buffer 引用。也就是说,此方法返回的 Buffer 引用的数据,是原 Buffer 的一个子集。并且,新的 Buffer 引用和原 Buffer 引用共享相同的数据。 |
public static ByteBuffer wrap(byte[] array) |
返回一个内容为 array 的 Buffer。此外,如果修改缓冲区的内容,array 也会随着改变,反之亦然。 |
public final Buffer flip() |
将写模式转换成读模式 |
public final Buffer rewind() |
用于重复读,具体重现参考源码 |
public final Buffer clear() |
清空 Buffer,具体重现参考源码 |
public final Buffer mark() |
标记:mark = position,具体重现参考源码 |
public final Buffer reset() |
重置:position = mark,具体重现参考源码 |
四 基本演示
1 代码
public class NIODemo {
public static void test1() {
ByteBuffer buffer = ByteBuffer.allocate(100);
System.out.println("---allocate----");
System.out.println("position:" + buffer.position()); // position:0
System.out.println("limit:" + buffer.limit()); // limit:100
System.out.println("capacity(定义之后,不会再改变):" + buffer.capacity()); // capacity(定义之后,不会再改变):100
// 向Buffer中存放数据
System.out.println("---put()----");
buffer.put("helloworld".getBytes());
System.out.println("position:" + buffer.position()); // position:10
System.out.println("limit:" + buffer.limit()); // limit:100
// 切换到读模式
System.out.println("---flip()----");
buffer.flip();
System.out.println("position:" + buffer.position()); // position:0
System.out.println("limit:" + buffer.limit()); // limit:10
// 从Buffer中读取数据
System.out.println("---get()----");
byte[] bs = new byte[buffer.limit()];
buffer.get(bs); // 读取数据,同时会后移position
System.out.println("读取到的数据:" + new String(bs)); // 读取到的数据:helloworld
System.out.println("position:" + buffer.position()); // position:10
System.out.println("limit:" + buffer.limit()); // limit:10
System.out.println("----slice()---");
buffer = ByteBuffer.allocate(8);
// buffer:0,1,2,3,4,5,6,7
for (int i = 0; i < buffer.capacity(); i++) {
buffer.put((byte) i);
}
buffer.position(2);
buffer.limit(6);
// sliceBuffer:2,3,4,5;获取从 position 到 limit之间 buffer的引用。
ByteBuffer sliceBuffer = buffer.slice();
// sliceBuffer 与 原Buffer 共享相同的数据;即修改 sliceBuffer 中的数据时,buffer 也会改变。
for (int i = 0; i < sliceBuffer.capacity(); i++) {
byte b = sliceBuffer.get(i);
b += 100;
sliceBuffer.put(i, b);
}
// 测试
System.out.println("当修改了 sliceBuffer 之后,查看 buffer:");
buffer.position(0);
buffer.limit(buffer.capacity());
while (buffer.hasRemaining()) {//{x,x,x,x,x,x} buffer.hasRemaining():判断是否有剩余元素
System.out.print(buffer.get() + ","); // 0,1,102,103,104,105,6,7,
}
System.out.println();
System.out.println("----mark--------");
ByteBuffer buffer2 = ByteBuffer.allocate(100);
buffer2.put("abcdefg".getBytes());
// 在此时的 position 位置处,做一个标记 mark
buffer2.mark();
System.out.println("position:" + buffer2.position()); // position:7
System.out.println("mark:" + buffer2.mark().position()); // mark:7
/*
通过get(byte[] dst, int offset, int length)方法,读取buffer中的“cde”。
注意,此方法可以直接从 Buffer 中的指定位置 offset 开始读取数据,而不需要flip()或rewind()。
*/
buffer2.get(bs, 2, 3);
buffer2.reset(); // 恢复到 position 的位置 7
System.out.println("position:" + buffer2.position()); // position:7
System.out.println("mark:" + buffer2.mark().position()); // mark:7
// 判断缓冲区是否有剩余数据
if (buffer2.hasRemaining()) {
System.out.println("Buffer中的剩余空间数:" + buffer2.remaining()); // Buffer中的剩余空间数:93
}
// 重复读rewind() : 1.postion=0,2.取消mark()
System.out.println("---rewind()----");
buffer2.rewind();
System.out.println("position:" + buffer2.position()); // position:0
// clear()"清空"缓冲区
System.out.println("-------clear()--------");
ByteBuffer buffer3 = ByteBuffer.allocate(100);
buffer3.put("abc".getBytes());
buffer3.clear(); // "清空"缓冲区 :position=0,但数据并没有真正被删除,只是处于废弃状态
System.out.println("position:" + buffer3.position()); // position:0
System.out.println("limit:" + buffer3.limit()); // limit:100
System.out.println("clear()之后,仍然可以获取到Buffer中的数据:" + (char) buffer3.get(1)); // clear()之后,仍然可以获取到Buffer中的数据:b
}
public static void main(String[] args) throws IOException {
test1();
}
}
2 测试结果
—allocate—-
position:0
limit:100
capacity(定义之后,不会再改变):100
—put()—-
position:10
limit:100
—flip()—-
position:0
limit:10
—get()—-
读取到的数据:helloworld
position:10
limit:10
—-slice()—
当修改了sliceBuffer之后,查看buffer:
0,1,102,103,104,105,6,7,
—-mark——–
position:7
mark:7
position:7
mark:7
Buffer中的剩余空间数:93
—rewind()—-
position:0
——-clear()——–
position:0
limit:100
clear()之后,仍然可以获取到Buffer中的数据:b
五 Buffer 存储各类型数据
1 代码
package nio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
import java.nio.charset.CharacterCodingException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class NIODemo {
// Buffer 存储各类型数据
public static void test() throws CharacterCodingException {
ByteBuffer buffer = ByteBuffer.allocate(32);
buffer.putDouble(3.14159);
buffer.putChar('程');
buffer.flip();
// 必须保证 get 和 put 的顺序一致
System.out.println(buffer.getDouble());
System.out.println(buffer.getChar());
}
public static void main(String[] args) throws IOException {
test();
}
}
2 测试
3.14159
程
今天的文章缓冲区是什么数据结构_数据的存储结构有哪些[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/84914.html