Java NIO 的緩衝區 Buffer 是什麼?

本篇是有關 Java NIO 的 Buffer 類別,請先參考先前的文章,先認識 Java NIO:

Java的NIO,什麼是blocking? 什麼是non-blocking?

緩衝區Buffer

Buffer 是 NIO 的一個重要的類別,其角色是介於遠端資料來源端與程式之間的「緩衝區」,當從 Channel 讀取資料時,先將資料置於 Buffer 中,程式再從 Buffer 中讀取資料。如下圖:

java.nio.Buffer 是個抽象類別,它制定了緩衝區該有的屬性與方法,並成為許多各類型緩衝區類別的父類別,例如專門用來存放位元組的 ByteBuffer、存放字元的 CharBuffer 等,常用的緩衝區類別有七種,分別對應除了布林值外的七個基本資料型別。

緩衝區的分類

緩衝區有「直接緩衝區」與「間接緩衝區」兩大類,直接緩衝區可跳過 JVM 的處理,直接對應到作業系統的記憶體來存取緩衝區資料,適合較大檔案或需要長存活時間的需求,因為 JVM 通常會因為本身被限制的記憶體使用量,而影響緩衝區的使用量。而間接緩衝區則是由 JVM 負責所有的容量分配與存取動作,使用上會耗費 JVM 本身所分配的記憶體,相對的比直接緩衝區的限制要來得多。

在實務的設計中,除非要存取的檔案較大,否則皆不採用直接緩衝區,因為在取得或清除該緩衝區時會花費較多的時間。通常都採用間接緩衝區來處理,產生間接緩衝區的方式是呼叫 Buffer 的靜態方法 allocate 來得到物件,例如要得到一個可容納 10 個 byte 的緩衝區物件:

ByteBuffer buf = ByteBuffer.allocate(20);

CharBuffer cbuf = CharBuffer.allocate(20);

要得到直接緩衝區的物件時,則要呼叫 ByteBuffer 的靜態方法 allocatDirect :

ByteBuffer buf = ByteBuffer.allocateDirect(20);

而且只能由 ByteBuffer 產生直接緩衝區(只有 ByteBuffer 有 allocateDirect 方法),再由 ByteBuffer 進行轉型得到其他類型的緩衝區,且 Buffer 皆可使用 isDirect() 得到目前的緩衝區是否為直接緩衝區:

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class BufferDirect {
	public static void main(String[] args) {
		//間接緩衝區
		ByteBuffer buf = ByteBuffer.allocate(20);
		System.out.println("是否是直接緩衝區?"+buf.isDirect());
		CharBuffer cbuf = CharBuffer.allocate(20);
		System.out.println("是否是直接緩衝區?"+cbuf.isDirect());
		//直接緩衝區
		ByteBuffer dbuf = ByteBuffer.allocateDirect(20);
		System.out.println("是否是直接緩衝區?"+dbuf.isDirect());
	}
}

程式執行結果:

是否是直接緩衝區?false
是否是直接緩衝區?false
是否是直接緩衝區?true

產生ByteBuffer

產生 ByteBuffer 有三種方式,除了上述呼叫 ByteBuffer 類別的靜態方法 allocate 以外,還可以由已存在的 byte 陣列「包裝(wrap)」而得到,另外使用 FileChannel 的 map 方法,則可將檔案資料對應成為緩衝區。

將資料放入緩衝區

準備好緩衝區物件後,可使用 put 方法將資料放入緩衝區,各個 put 方法的使用說明如下:

  • ByteBuffer put(byte[] src)

將一個 byte 陣列的資料放入緩衝區,並回傳該緩衝區,例如:

ByteBuffer buf = ByteBuffer.allocate(10);
byte[] bb = "abcxyz".getBytes();
buf.put(bb);

buf的內容將成為:

  • ByteBuffer put(byte[] src, int offset, int length)

將 byte 陣列中特定範圍的元素放入緩衝區,從陣列的 offset 位置開始取得 length 個元素,並回傳該緩衝區,例如取得 “abcxyz” 字串中的 “yz”,並放入 ByteBuffer 中:

ByteBuffer buf = ByteBuffer.allocate(10);
byte[] bb = "abcxyz".getBytes();
buf.put(bb, 4, 2);

buf 的內容將成為:

  • ByteBuffer put(ByteBuffer src)

將另一個 ByteBuffer 的內容放到目前的 ByteBuffer 中,回傳該緩衝區,例如將一個緩衝區 buf 的內容放到另一個 buf2 中:

ByteBuffer buf = ByteBuffer.allocate(10);
byte[] bb = "abcxyz".getBytes();
buf.put(bb);
ByteBuffer buf2 = ByteBuffer.allocate(10);
buf2.put(buf);

buf2 的內容將成為:

Comments

No comments yet. Why don’t you start the discussion?

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *