Java NIO中的緩沖區Buffer(一)緩沖區基礎


什么是緩沖區(Buffer)

定義

簡單地說就是一塊存儲區域,哈哈哈,可能太簡單了,或者可以換種說法,從代碼的角度來講(可以查看JDK中Buffer、ByteBuffer、DoubleBuffer等的源碼),Buffer類內部其實就是一個基本數據類型的數組,以及對這個緩沖數組的各種操作;

常見的緩沖區如ByteBuffer、IntBuffer、DoubleBuffer...內部對應的數組依次是byte、int、double...

與通道的關系

在Java NIO中,緩沖區主要是跟通道(Channel)打交道,數據總是從緩沖區寫入到通道中,或者從通道讀取數據到緩沖區;

繼承結構

關於Buffer的繼承結構,我們可以簡單的以ByteBuffer為例,如下:

Buffer是頂層抽象類,ByteBuffer繼承Buffer,也是抽象類,ByteBuffer最常見的兩個具體實現類如下:

DirectByteBuffer(JVM堆外部、通過unsafe.allocateMemory實現)、HeapByteBuffer(JVM堆

緩沖區的四個屬性(capacity、limit、position、mark)

容量(capacity)

capacity指的是緩沖區能夠容納元素的最大數量,這個值在緩沖區創建時被設定,而且不能夠改變,如下,我們創建了一個最大容量為10的字節緩沖區;

ByteBuffer bf = ByteBuffer.allocate(10);

上界(limit)

limit指的是緩沖區中第一個不能讀寫的元素的數組下標索引,也可以認為是緩沖區中實際元素的數量;

位置(position)

position指的是下一個要被讀寫的元素的數組下標索引,該值會隨get()和put()的調用自動更新;

標記(mark)

一個備忘位置,調用mark()方法的話,mark值將存儲當前position的值,等下次調用reset()方法時,會設定position的值為之前的標記值;

四個屬性值之間的關系

根據以上四個屬性的定義,我們可以總結出它們之間的關系如下:

0 <= mark <= position <= limit <= capacity

舉個例子,觀察四個屬性值的變化

 1、創建一個容量大小為10的字符緩沖區

ByteBuffer bf = ByteBuffer.allocate(10);

此時:mark = -1; position = 0; limit = 10; capacity = 10;

2、往緩沖區中put()五個字節

bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'0');

注意這里一個字符是占用兩個字節的,但是英文字符只占用一個字節,所以這樣是可以實現儲存效果的;

此時:mark = -1; position = 5; limit = 10; capacity = 10;

3、調用flip()方法,切換為讀就緒狀態

bf.flip();

此時:mark = -1; position = 0; limit = 5; capacity = 10;

 

 4、讀取兩個元素

System.out.println("" + (char) bf.get() + (char) bf.get());

 此時:mark = -1; position = 2; limit = 5; capacity = 10;

5、標記此時的position位置

bf.mark();

此時:mark = 2; position = 2; limit = 5; capacity = 10;

6、讀取兩個元素后,恢復到之前mark的位置處

System.out.println("" + (char) bf.get() + (char) bf.get());
bf.reset();

屬性變化情況:

執行完第一行代碼:mark = 2; position = 4; limit = 5; capacity = 10;

執行完第二行代碼:mark = 2; position = 2; limit = 5; capacity = 10;

7、調用compact()方法,釋放已讀數據的空間,准備重新填充緩存區

bf.compact();

此時:mark = 2; position = 3; limit = 10; capacity = 10;

 注意觀察數組中元素的變化,實際上進行了數組拷貝,拋棄了已讀字節元素,保留了未讀字節元素;

 緩沖區比較

其實查看equals源碼就可以知道是如何比較的,如下(以ByteBuffer為例):

public boolean equals(Object ob) {
        if (this == ob)
            return true;
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
            if (!equals(this.get(i), that.get(j)))
                return false;
        return true;
    }

總的來說,兩個緩沖區被認為相等的條件如下(以下內容直接摘自《Java NIO》):

  1. 兩個對象類型相同。包含不同數據類型的 buffer 永遠不會相等,而且 buffer絕不會等於非 buffer 對象。
  2. 兩個對象都剩余同樣數量的元素。Buffer 的容量不需要相同,而且緩沖區中剩余數據的索引也不必相同。但每個緩沖區中剩余元素的數目(從位置到上界)必須相同。
  3. 在每個緩沖區中應被 Get()方法返回的剩余數據元素序列必須一致。

批量讀寫緩沖區數據

以ByteBuffer為例,使用如下API即可:

public ByteBuffer get(byte[] dst, int offset, int length)

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

public ByteBuffer get(byte[] dst)

public final ByteBuffer put(byte[] src)

實際上,后面兩種方法內部就是調用前面兩種方法的;

參數的含義直接查看源碼注釋即可,寫的很清楚,如put(byte[] src, int offset, int length)方法的注釋:

    /* @param  src
     *         The array from which bytes are to be read
     *
     * @param  offset
     *         The offset within the array of the first byte to be read;
     *         must be non-negative and no larger than <tt>array.length</tt>
     *
     * @param  length
     *         The number of bytes to be read from the given array;
     *         must be non-negative and no larger than
     *         <tt>array.length - offset</tt>
     */

 參考資料

《Java NIO》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM