NIO 之 Buffer 圖解

可參考之前的文章:NIO 之 ByteBuffer實現原理
下面是對之前文章的一個補充

Buffer 類 結構

對於每個非布爾原始數據類型都有一個緩衝區類。儘管緩衝區作用於它們存儲的原始數據類型,但緩衝區十分傾向於處理字節。

概述

緩衝區 Buffer 內部就是用數組實現的。 Buffer 包含了下面4個屬性:

  • 容量( Capacity)
    緩衝區能夠容納的數據元素的最大數量。這一容量在緩衝區創建時被設定,並且永遠不能被改變。
  • 上界( Limit)
    緩衝區的第一個不能被讀或寫的元素。或者說,緩衝區中現存元素的計數。
  • 位置( Position)
    下一個要被讀或寫的元素的索引。位置會自動由相應的 get( )和 put( )函數更新。
  • 標記( Mark)
    一個備忘位置。調用 mark( )來設定 mark = postion。調用 reset( )設定 position = mark。標記在設定前是未定義的(undefined)。

這四個屬性之間總是遵循以下關係:
0 <= mark <= position <= limit <= capacity

示例

下面展示了一個新創建的容量爲 10 的 ByteBuffer 邏輯視圖

ByteBuffer.allocate(10);

圖1

位置(Position)被設爲 0,而且容量( Capacity)和上界( Limit)被設爲 10,剛好經過緩衝區能夠容納的最後一個字節。
標記(mark)最初未定義。
容量(Capacity)是固定的,但另外的三個屬性可以在使用緩衝區時改變。

put() 方法

讓我們看一個例子。 我們將代表“abcde”字符串的 ASCII 碼載入一個名爲 buffer 的
ByteBuffer 對象中。當在圖1 中所新建的緩衝區上執行以下代碼後。

buffer.put((byte)'a').put((byte)'b').put((byte)'c').put((byte)'d').put((byte)'e');

緩衝區的結果狀態如圖 2所示:
圖2

flip() 方法

我們已經寫滿了緩衝區,現在我們必須準備將其清空。我們想把這個緩衝區傳遞給一個通
道,以使內容能被全部寫出。但如果通道現在在緩衝區上執行 get(),那麼它將從我們剛剛插入的有用數據之外取出未定義數據。如果我們將位置值重新設爲 0,通道就會從正確位置開始獲取,但是它是怎樣知道何時到達我們所插入數據末端的呢?這就是上界屬性被引入的目的。上界屬性指明瞭緩衝區有效內容的末端。我們需要將上界屬性設置爲當前位置,然後將位置重置爲 0。

flip()函數將一個能夠繼續添加數據元素的填充狀態的緩衝區翻轉成一個準備讀出元素
的釋放狀態。在翻轉之後,圖 2 的緩衝區會變成圖 3 中的樣子。

圖3

rewind() 方法

rewind()函數與 flip()相似,但不影響上界屬性。它只是將位置值設回 0。您可以使
用 rewind()後退,重讀已經被翻轉的緩衝區中的數據。
圖2 的緩衝區調用 rewind() 方法會變成圖4 中的樣子。
圖4

如果將緩衝區翻轉兩次會怎樣呢?

compact() 方法

有時,您可能只想從緩衝區中釋放一部分數據,而不是全部,然後重新填充。爲了實現這
一點,未讀的數據元素需要下移以使第一個元素索引爲 0。儘管重複這樣做會效率低下,但這有時非常必要,而 API 對此爲您提供了一個 compact()函數。這一緩衝區工具在複製數據時要比您使用 get()和 put()函數高效得多。所以當您需要時,請使用 compact()。圖 5顯示了一個讀取了兩個元素(position 現在爲2),並且現在我們想要對其進行壓縮的緩衝區。

圖5

buffer.compact();

壓縮後的結果如下圖
圖6

duplicate() 方法

duplicate() 方法創建了一個與原始緩衝區一樣的新緩衝區。兩個緩衝區共享數據,擁有同樣的 capacity ,但每個緩衝區都擁有自己的 position,limit 和 mark 屬性。對一個緩衝區內的數據元素所做的改變會反映在另外一個緩衝區上。這一副本緩衝區具有與原始緩衝區同樣的數據視圖。如果原始的緩衝區爲只讀,或者爲直接緩衝區,新的緩衝區將繼承這些屬性。

    public ByteBuffer duplicate() {
        return new HeapByteBufferR(hb,
                                        this.markValue(),
                                        this.position(),
                                        this.limit(),
                                        this.capacity(),
                                        offset);
    }

重新創建一個 ByteBuffer,並且使用同一個數組。所有一個byteBuffer 變動,會影響另一個 ByteBuffer。 但 position、limit、mark 都是獨立的。

duplicate() 方法

您 可 以 使 用 asReadOnlyBuffer() 函 數 來 生 成 一 個 只 讀 的 緩 衝 區 視 圖 。 這 與
duplicate()相同,除了這個新的緩衝區不允許使用 put(),並且其 isReadOnly()函數
將 會 返 回 true 。 對 這 一 只 讀 緩 衝 區 的 put() 函 數 的 調 用 嘗 試 會 導 致 拋 出
ReadOnlyBufferException 異常。

public ByteBuffer asReadOnlyBuffer() {
        return new HeapByteBufferR(hb,
                                     this.markValue(),
                                     this.position(),
                                     this.limit(),
                                     this.capacity(),
                                     offset);
}
HeapByteBufferR 分析
class HeapByteBufferR
    extends HeapByteBuffer{
    public ByteBuffer put(byte x) {
        throw new ReadOnlyBufferException();
    }

    public ByteBuffer put(int i, byte x) {
        throw new ReadOnlyBufferException();
    }

    public ByteBuffer putInt(int x) {
        throw new ReadOnlyBufferException();
    }
    ......
}

HeapByteBufferR 繼承 HeapByteBuffer 類,並重寫了所有的可修改 buffer 的方法。把所有能修改 buffer 的方法都直接 throw ReadOnlyBufferException,來保證只讀。

slice() 方法

slice() 分割緩衝區。創建一個從原始緩衝區的當前位置開始的新緩衝區,並且其容量是原始緩衝區的剩餘元素數量( limit-position)。這個新緩衝區與原始緩衝區共享一段數據元素子序列。分割出來的緩衝區也會繼承只讀和直接屬性。

原 ByteBuffer如下圖:

slice() 分割後的 ByteBuffer

    public ByteBuffer slice() {
        return new HeapByteBuffer(hb,
                                        -1,
                                        0,
                                        this.remaining(),
                                        this.remaining(),
                                        this.position() + offset);
    }

想了解更多精彩內容請關注我的公衆號

本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8    
點擊這裏快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這裏快速進入GIT

發佈了132 篇原創文章 · 獲贊 79 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章