<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 40, comments - 58, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    對 java.nio.ByteBuffer 的理解

    Posted on 2011-05-18 10:53 Astro.Qi 閱讀(412) 評論(4)  編輯  收藏 所屬分類: Java

    今晚用到 ByteBuffer, 我跟 joy 都是初學 java, 文檔里的中文翻譯實在是看他母親不懂, 暈了半天, 作了幾個測試, 終于把這個類的用法搞清楚了, 順便臆想了哈其工作原理.

    先列點代碼片段:
    // ...
    //
    // 此段代碼功能為從 t.txt 里復制所有數據到 out_j.txt:
    //
    ...
    1 FileChannel fcin = new FileInputStream( "d:/t.txt" ).getChannel();
    2 FileChannel fcout = new FileOutputStream( new File( "d:/out_j.txt" )).getChannel();
    3 ByteBuffer buff = ByteBuffer.allocate( 1024 );
    4 long t1 = System.currentTimeMillis();
    5
    6 while( fcin.read( buff ) != -1 )
    7 {
    8   buff.flip();
    9   fcout.write( buff );
    10   buff.clear();
    11 }
    12
    13 long t2 = System.currentTimeMillis();
    14 long size = fcin.size();
    15 javax.swing.JOptionPane.showMessageDialog( null, size + " 字節, 耗時 " + ( t2 - t1 + 1 ) + " ms." );
    ...

    ----------------------------------------------------------------------------------------------------

    SDK 文檔里對 ByteBuffer 的說明為:

    public abstract class ByteBuffer
    extends Buffer
    implements Comparable <ByteBuffer>

    這說明 ByteBuffer 是繼承于 Buffer 的抽象類, 實現了兩個接口.

    行3 通過 allocate() 分配了一塊 1024 字節的緩沖區, 并返回一個 ByteBuffer 對象. (抽象類不能直接 new)
    行6 fcin.read() 將數據讀入到 buff. 此處的 read() 是 FileChannel 類的一個虛函數.
    行8 buff.flip() 這個調用就是開頭一直無法理解的部分.

    ----------------------------------------------------------------------------------------------------

    SDK 文檔里的對 flip() 的說明是:

    public final Buffer flip()
    反轉此緩沖區。首先對當前位置設置限制,然后將該位置設置為零。如果已定義了標記,則丟棄該標記。
    當將數據從一個地方傳輸到另一個地方時,經常將此方法與 compact 方法一起使用。

    我最終的理解是: 文檔翻譯得太差了, 把不應該翻譯的內容也譯成了中文, 所以反而不容易理解.
    關鍵就在以下 2 處:

    當前位置: 這個可以直觀地理解為緩沖區中的當前數據指針, 或是 SQL 中的游標, 記為 curPointer.
    限制: 這個可以理解成實際操作的緩沖區段的結束標記, 記為 endPointer.
    反轉: 這個完全是對 flip 這個詞不負責的翻譯, 如果參照 DirectX 里的 flip() 而譯為翻轉/翻頁, 那就好理解得多, 就像寫信/看信, 寫/看完一頁后, 翻到下一頁, 眼睛/筆從頁底重新移回頁首.
    這個翻轉背后的操作其實就是 "把 endPointer 定位到 curPointer 處, 并把 curPointer 設為 0".

    關于標記, 在這里不涉及. 下一句說到常與 compact 方法一起使用, 是可以想像的, 因為 compact 方法對數據進
    行了壓縮, 有效數據的真實長度發生了變化, 肯定需要用 flip 重新定位結束標記.

    在填充, 壓縮等數據操作時, curPointer 估計都是自動更新了位置的, 總是指向最后一個有效數據, 所以每次調
    用 flip() 后, endPointer 就指向了有效數據的結尾, 而 curPointer 指向了 0 (緩沖起始處).

    舉個圖例:
    (c 和 e 分別代表 curPointer 和 endPointer 兩個指針)

    * 先是一個空的 ByteBuffer (大小為 10 字節)
    -------------------
    -------------------
    c
    e


    * 然后填充 5 字節數據
    -------------------
    0 1 2 3 4
    -------------------
    e           c
    此時, endPointer 尚在 0 處, curPointer 移到了數據結尾.
    經測試, 此時若取數據, 將得到 5 個字節, 內容通常為 0 (也有可能是未知), 因為實際上取到的是從 c 處到緩沖
    區實際結束處的 5 個未初始化的字節.

    * 調用一次 flip() 后
    -------------------
    0 1 2 3 4
    -------------------
    c           e
    此時, endPointer 先被移到 curPointer, 然后 curPointer 移到 0.
    通過測試可見, ByteBuffer 取數據時, 是從 curPointer 起, 到 endPointer 止, 若 curPointer > endPointer, 則取到緩沖區結束.


    再看上面代碼的關鍵片段, 行 8 處調用 flip() 即有兩個作用, 一是將 curPointer 移到 0, 二是將 endPointer 移到有效數據結尾.

    此行可由以下兩行代替:
    buff.limit( buff.position());
    buff.position( 0 );

    可見對其工作原理的理解, 應該是正確的.

    ----------------------------------------------------------------------------------------------------

    總結如下:
    1. put 數據時, 不會自動清除緩沖區中現有的數據.
    2. 每一次 get 或 put 后, curPointer 都將向緩沖區尾部移動, 移動量=操作的數據量.
    3. get/put 均是從 curPointer 起, 到 curPointer + 操作的數據長度止.
    4. get/put 操作中, 若 curPointer 超過了 endPointer 或緩沖區總長度, 將拋出 java.nio.BufferUnderflowException 異常.

    注: curPointer 和 endPointer 只是為文中方便描述命名的, 實際分別對應到 ByteBuffer.position() 和 ByteBuffer.limit() 兩個方法.

    ----------------------------------------------------------------------------------------------------

    疑惑:
    curPointer 是用 ByteBuffer.position() 取值, 用 ByteBuffer.position( int ) 賦值, 不知道 JDK 為什么要用多態來實現這兩個功能, 按我的想法, 設計成 getPosition(), setPosition() 不是要好看好記得多啊.

    ----------------------------------------------------------------------------------------------------

    跟 C++ 的簡單比較:
    C++ 里面沒有類似 ByteBuffer 的現成實現, 實現上述類似的文件復制功能, 通常要自己創建管理緩沖區. C++ 里讀寫文件通常用 FileRead(), FileWrite() 函數, 在讀/寫的時候, 可以直接指定讀/寫的數據長度, 相比下顯得
    直觀方便些, 但 JDK 這個 ByteBuffer 的方式, 確實更方便好用.

    ByteBuffer 作為繼承自 Buffer 的抽象類, 實現了對 Byte 型緩沖的管理, 同時 JDK 里還有對應其他數據類型的
    繼承自 Buffer 的抽象類, 分別實現對應類型的緩沖管理. 這種設計減少了編程時的工作. 如果在 C++ 中, 調用
    讀/寫函數時, 還需要考慮傳入數據的類型, 通常用傳入 sizeof(數據類型) 的方式指定, 除了函數調用時增加耗
    費外, 靈活性也更差些.

    反過來再想, 為什么 C 要用這種方式? 個人認為, 這是 C 標準庫的實現方式, 因為在不同 OS 平臺上, 對文件和
    設備的訪問方法在系統層不一定相同, 同時硬件平臺(主要是 CPU)上基本數據類型寬度也有可能不同, 標準庫通過
    sizeof 這個宏在編譯時才能確定數據寬度, 所以標準 C 代碼通常可以在不同平臺上重新編譯.

    再想 C++ 為什么要用這種方式? 首先, C++ 里沿用 C 標準庫的模式是可以理解的, 其次, 也許只是 C++ 標準庫
    里沒有類似的設計, 說不定早就有第三方通過模板實現的了.

    個人認為, ByteBuffer 在實現上, 可以算是一種數據結構, 在類設計上, 可以算是一種設計模式了.

    評論

    # re: 對 java.nio.ByteBuffer 的理解[未登錄]  回復  更多評論   

    2014-04-07 11:21 by 1
    1

    # re: 對 java.nio.ByteBuffer 的理解[未登錄]  回復  更多評論   

    2014-04-07 11:21 by 1
    11

    # re: 對 java.nio.ByteBuffer 的理解[未登錄]  回復  更多評論   

    2014-04-07 11:21 by 1
    1

    # re: 對 java.nio.ByteBuffer 的理解[未登錄]  回復  更多評論   

    2014-04-07 11:22 by 1
    12
    主站蜘蛛池模板: 亚洲日韩人妻第一页| 久久久综合亚洲色一区二区三区| 特级毛片aaaa级毛片免费| 国产AV无码专区亚洲AV漫画| 青柠影视在线观看免费| 亚洲色欲色欲www| 久久久久亚洲AV成人网| 久草视频在线免费| 男女男精品网站免费观看 | 国产成人在线观看免费网站| 一个人看www免费高清字幕| 亚洲伊人tv综合网色| 免费国产成人高清在线观看麻豆| 最好免费观看高清在线| 亚洲熟妇AV一区二区三区浪潮| av无码东京热亚洲男人的天堂| 最好看最新的中文字幕免费| 国产亚洲精品仙踪林在线播放| 亚洲图片在线观看| 亚洲国产精品综合久久一线| 18pao国产成视频永久免费| 免费无码又爽又黄又刺激网站| 亚洲网站免费观看| 久久久久亚洲AV无码专区网站| 免费精品国产自产拍在| APP在线免费观看视频| 色噜噜的亚洲男人的天堂| 亚洲成av人片不卡无码| 国产精品亚洲美女久久久| 搡女人免费视频大全| 免费国产黄网站在线观看| 少妇亚洲免费精品| 亚洲日本VA午夜在线影院| 少妇中文字幕乱码亚洲影视| 久久久精品国产亚洲成人满18免费网站 | 亚洲日韩涩涩成人午夜私人影院| 九九精品免费视频| 日韩免费人妻AV无码专区蜜桃| 日日摸夜夜添夜夜免费视频| 亚洲AV无码一区二区三区牛牛| 久久亚洲精品成人av无码网站|