NIO 之 Buffer 图解
可參考之前的文章:NIO 之 ByteBuffer實現原理
下面是對之前文章的一個補充
Buffer 類 結構
對于每個非布爾原始數據類型都有一個緩沖區類。盡管緩沖區作用于它們存儲的原始數據類型,但緩沖區十分傾向于處理字節。
概述
緩沖區 Buffer 內部就是用數組實現的。 Buffer 包含了下面4個屬性:
- 容量( Capacity)
緩沖區能夠容納的數據元素的最大數量。這一容量在緩沖區創建時被設定,并且永遠不能被改變。 - 上界( Limit)
緩沖區的第一個不能被讀或寫的元素?;蛘哒f,緩沖區中現存元素的計數。 - 位置( Position)
下一個要被讀或寫的元素的索引。位置會自動由相應的 get( )和 put( )函數更新。 - 標記( Mark)
一個備忘位置。調用 mark( )來設定 mark = postion。調用 reset( )設定 position = mark。標記在設定前是未定義的(undefined)。
這四個屬性之間總是遵循以下關系:
0 <= mark <= position <= limit <= capacity
示例
下面展示了一個新創建的容量為 10 的 ByteBuffer 邏輯視圖
ByteBuffer.allocate(10);位置(Position)被設為 0,而且容量( Capacity)和上界( Limit)被設為 10,剛好經過緩沖區能夠容納的最后一個字節。
標記(mark)最初未定義。
容量(Capacity)是固定的,但另外的三個屬性可以在使用緩沖區時改變。
put() 方法
讓我們看一個例子。 我們將代表“abcde”字符串的 ASCII 碼載入一個名為 buffer 的
ByteBuffer 對象中。當在圖1 中所新建的緩沖區上執行以下代碼后。
緩沖區的結果狀態如圖 2所示:
flip() 方法
我們已經寫滿了緩沖區,現在我們必須準備將其清空。我們想把這個緩沖區傳遞給一個通
道,以使內容能被全部寫出。但如果通道現在在緩沖區上執行 get(),那么它將從我們剛剛插入的有用數據之外取出未定義數據。如果我們將位置值重新設為 0,通道就會從正確位置開始獲取,但是它是怎樣知道何時到達我們所插入數據末端的呢?這就是上界屬性被引入的目的。上界屬性指明了緩沖區有效內容的末端。我們需要將上界屬性設置為當前位置,然后將位置重置為 0。
flip()函數將一個能夠繼續添加數據元素的填充狀態的緩沖區翻轉成一個準備讀出元素
的釋放狀態。在翻轉之后,圖 2 的緩沖區會變成圖 3 中的樣子。
rewind() 方法
rewind()函數與 flip()相似,但不影響上界屬性。它只是將位置值設回 0。您可以使
用 rewind()后退,重讀已經被翻轉的緩沖區中的數據。
圖2 的緩沖區調用 rewind() 方法會變成圖4 中的樣子。
如果將緩沖區翻轉兩次會怎樣呢?
compact() 方法
有時,您可能只想從緩沖區中釋放一部分數據,而不是全部,然后重新填充。為了實現這
一點,未讀的數據元素需要下移以使第一個元素索引為 0。盡管重復這樣做會效率低下,但這有時非常必要,而 API 對此為您提供了一個 compact()函數。這一緩沖區工具在復制數據時要比您使用 get()和 put()函數高效得多。所以當您需要時,請使用 compact()。圖 5顯示了一個讀取了兩個元素(position 現在為2),并且現在我們想要對其進行壓縮的緩沖區。
壓縮后的結果如下圖
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 異常。
HeapByteBufferR 分析
class HeapByteBufferRextends 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
想了解更多精彩內容請關注我的公眾號
本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
點擊這里快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這里快速進入GIT
總結
以上是生活随笔為你收集整理的NIO 之 Buffer 图解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈UML类图中类之间的5种关系
- 下一篇: NIO 之 MappedByteBuff