一、緩衝區基礎
概念上,緩衝區是包在一個對象內的基本數據元素數組。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);
}
}