NIO 数据存储结构——缓冲区 Buffer

x33g5p2x  于2022-05-18 转载在 其他  
字(6.7k)|赞(0)|评价(0)|浏览(211)

一 点睛

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 为例进行说明

| <br>方法<br> | <br>简介<br> |
| <br>public static ByteBuffer allocate(int capacity)<br> | <br>分配大小为 capacity 的非直接缓冲区(单位byte)<br> |
| <br>public static ByteBuffer allocateDirect(int capacity)<br> | <br>分配大小为 capacity 的直接缓冲区(单位byte)<br> |
| <br>public abstract ByteBuffer put(byte b)  <br><br>public abstract ByteBuffer put(int index, byte b)<br><br>public final ByteBuffer put(byte[] src)<br><br>public ByteBuffer put(byte[] src, int offset, int length)<br> | <br>向缓冲区中存放数据<br> |
| <br>public abstract byte get()<br><br>public abstract byte get(int index)<br><br>public ByteBuffer get(byte[] dst)<br><br>public ByteBuffer put(byte[] src, int offset, int length)<br> | <br>从缓存区中读取数据<br> |
| <br>public abstract ByteBuffer asReadOnlyBuffer()<br> | <br>将一个 Buffer 转为一个只读 Buffer。之后,不能再对转换后的 Buffer 进行写操作<br> |
| <br>public abstract ByteBuffer slice()<br> | <br>将原 Buffer 从 position 到 limit 之间的部分数据交给一个新的 Buffer 引用。也就是说,此方法返回的 Buffer 引用的数据,是原 Buffer 的一个子集。并且,新的 Buffer 引用和原 Buffer 引用共享相同的数据。<br> |
| <br>public static ByteBuffer wrap(byte[] array)<br> | <br>返回一个内容为 array 的 Buffer。此外,如果修改缓冲区的内容,array 也会随着改变,反之亦然。<br> |
| <br>public final Buffer flip()<br> | <br>将写模式转换成读模式<br> |
| <br>public final Buffer rewind()<br> | <br>用于重复读,具体重现参考源码<br> |
| <br>public final Buffer clear()<br> | <br>清空 Buffer,具体重现参考源码<br> |
| <br>public final Buffer mark()<br> | <br>标记:mark = position,具体重现参考源码<br> |
| <br>public final Buffer reset()<br> | <br>重置:position = mark,具体重现参考源码<br> |

四 基本演示

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

相关文章