一、缓冲区基础
概念上,缓冲区是包在一个对象内的基本数据元素数组。Buffer 类相比一个简单数组的有点是它将关于数据的数据内容和信息包含在一个单一的对象中。Buffer 类以及它专有的子类定义了一个用于处理数据缓冲区的 API。
所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。它们是:
1. 容量(Capacity)
缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
2. 上界(Limit)
缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
3. 位置(Position)
下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。
4. 标记(Mark)
一个备忘位置。调用 mark() 来设定 mark = position。调用 reset() 设定 position = mark。标记在设定前是未定义的(undifined)。
缓冲区的分类
- 字节缓冲区 ByteBuffer
- 字符缓冲区 CharBuffer
- 双精浮点型(double)缓冲区 DoubleBuffer
- 单精浮点型(float)缓冲区 FloatBuffer
- 整型(int)缓冲区 IntBuffer
- 长整型(long)缓冲区 LongBuffer
- 短整型(short)缓冲区 ShortBuffer
上述的各类型的缓冲区都提供了读和写的方法(get & put),也提供了一些批量的 put 和 get 方法。而且缓冲区可以通过 allocation 创建,此方法通过warpping 将一个现有(数据类型)数组包装到缓冲区中来为缓冲区内容分配空间,或者通过创建现有字节缓冲区的视图来创建。注意,没有布尔型的缓冲区。
缓冲区的操作
- 存:通过相关 Buffer 类的 put 方法进行操作。
- 取:通过相关 Buffer 类的 get 方法进行操作。
还有其他一些 get/put 方法的重载方式。
二、创建缓冲区
java.nio包中有七种主要的缓冲区类,每一种都具有一种Java语言中的非布尔类型的原始类型数据。这些类没有一种能够直接实例化,它们都是抽象类,但是都包含静态工厂方法用来创建相应类的实例。
虽然有七种类型的缓冲区类,但是它们的创建方式基本都是类似的。新的缓冲区是由分配或包装操作创建的。分配操作创建一个缓冲区对象并分配一个私有的空间来储存容量大小的数据元素。包装操作创建一个缓冲区对象但是不分配任何空间来储存数据元素。它使用你所提供的数组作为存储空间来储存缓冲区中的数据元素。
要分配一个容量为100个 char 变量的 CharBuffer:
CharBuffer charBuffer = CharBuffer.allocate(100);
如果你想提供你自己的数组用作缓冲区的备份存储器,请调用 wrap() 函数:
char[] myArray = new char[100];
CharBuffewr charbuffer = CharBuffer.wrap(myArray);
通过 allocate() 或者 wrap() 函数创建的缓冲区通常都是间接的。间接的缓冲区使用备份数组,像我们之前讨论的,你可以通过上面列出的 API 函数获得对这些数组的存取权。Boolean 型函数 hasArray() 告诉你这个缓冲区是否有一个可存取的备份数组。如果这个函数返回 true,array() 函数会返回这个缓冲区对象所使用的数组存储空间的引用。
三、复制缓冲区
如我们刚刚讨论的那样,可以创建描述从外部存储到数组中的数据元素的缓冲区对象。但是缓冲区不限于管理数组中的外部数据。它们也能管理其他缓冲区中的外部数据。当一个管理其它缓冲器所包含的数据元素的缓冲器被创建时,这个缓冲器被称为视图缓冲器。大多数的视图缓冲器都是 ByteBuffer 的视图。在继续前往字节缓冲器的细节之前,我们先将注意力放在所有存储器类型的共同视图上。
视图存储器总是通过调用已存在的存储器实例中的函数来创建。使用已存在的存储器实例中的工厂方法意味着视图对象为原始存储器的内容实现细节私有。数据元素可以直接存取,无论它们是存储在数组中还是以一些其他的方式,而不需经过原始缓冲区对象的 get/put API。如果原始缓冲区是直接缓冲区,该缓冲区的视图会具有同样的效率优势。映像缓冲区也是如此。
Duplicate() 函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。这一副本缓冲区具有与原始缓冲区同样的数据视图。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。
注意:复制一个缓冲区会创建一个新的 Buffer 对象,但并不复制数据。原始缓冲区和副本都会操作同样的数据元素。
四、示例代码
package me.zhengzx.nio;
import java.nio.IntBuffer;
public class TestBuffer {
public static void main(String[] args) {
//创建指定长度的缓冲区
IntBuffer buffer = IntBuffer.allocate(100);
int[] array = new int[]{3, 5, 1};
//使用数组来创建一个缓冲区视图[3, 5, 1]
buffer = IntBuffer.wrap(array);
//利用数组的某一个区间创建视图
//buffer = buffer.wrap(array, 0, 2);
//对缓冲区某个位置上面进行元素修改
buffer.put(0, 7);
//遍历和缓冲区中数据
System.out.println("缓冲区数据如下:");
for(int i = 0; i < buffer.limit(); i++) {
System.out.print(buffer.get() + "\t"); //get会递增pos
}
//遍历数组中元素,结果表明缓冲区的修改,也会直接影响到原数组的数据
System.out.println("\n原始数据如下:");
for(int a : array) {
System.out.print(a + "\t");
}
//buffer.flip(); //对缓冲区进行反转(limit = pos; pos = 0)
//buffer.clear();
//打印对象信息
System.out.println("\nBuffer类信息:" + buffer);
//赋值一个新的缓冲区
IntBuffer buffer2 = buffer.duplicate();
System.out.println("\nBuffer类信息:" + buffer2);
}
}