Java ByteBuffer –速成課程

以我的經驗,當開發人員第一次遇到java.nio.ByteBuffer時,會引起混亂和細微的錯誤,因為如何正確使用它尚不明顯。 在我對API文檔感到滿意之前,需要反復閱讀API文檔和一些經驗以實現一些微妙之處。 這篇文章是關于如何正確使用它們的短暫崩潰,希望可以為其他人節省一些麻煩。

由于所有這些都是基于推斷(而不是基于明確的文檔),并且是基于經驗,因此我不能斷言這些信息必定是權威的。 我歡迎您提出反饋意見,以指出錯誤或其他觀點。 我也歡迎提出其他陷阱/最佳做法的建議。

我確實假定讀者將閱讀與本文相關的API文檔。 我不會窮盡所有您可以使用ByteBuffer進行的操作。

ByteBuffer抽象

可以將ByteBuffer看作提供了一些(未定義的)底層字節存儲的視圖 。 字節緩沖區的兩種最常見的具體類型是由字節數組支持的字節緩沖區和由直接(脫離堆,本機)字節緩沖區支持的字節緩沖區。 在兩種情況下,都可以使用相同的接口讀取和寫入緩沖區的內容。

ByteBuffer的API的某些部分特定于某些類型的字節緩沖區。 例如,字節緩沖區可以是只讀的 ,將用法限制為方法的子集。 array()方法僅適用于由字節數組支持的字節緩沖區(可以使用hasArray()進行測試),并且通常僅在完全知道自己在做什么的情況下使用 。 一個常見的錯誤是使用array()將ByteBuffer“轉換”為字節數組。 這不僅僅適用于字節數組支持的緩沖區,而且很容易成為錯誤的來源,因為根據緩沖區的創建方式,返回數組的開頭可能與字節緩沖區的開頭相對應, 也可能不對應。 結果往往是一個細微的錯誤,其中代碼的行為根據字節緩沖區和創建它的代碼的實現細節而有所不同。

ByteBuffer可以通過調用repeat()復制自身。 這實際上并不復制基礎字節 ,而只是創建一個指向相同基礎存儲的新ByteBuffer實例。 可以使用slice()創建表示另一個ByteBuffer的子集的ByteBuffer。

與字節數組的主要區別

  • ByteBuffer具有關于hashCode() / equals()的值語義,因此可以更方便地在容器中使用。
  • ByteBuffer通過實例化新的ByteBuffer,提供了將字節緩沖區的子集作為值傳遞而不復制字節的功能。
  • NIO API大量使用了ByteBuffer:s。
  • ByteBuffer中的字節可能駐留在Java堆之外。
  • ByteBuffer的狀態超出了字節本身,這有利于進行相對的I / O操作(但有一些警告,請參見下文)。
  • ByteBuffer提供了用于讀取和寫入各種原始類型(如整數和long)的方法(并且可以按不同的字節順序進行操作)。

ByteBuffer的關鍵屬性

ByteBuffer的以下三個屬性至關重要(我在每個屬性上引用了API文檔):

  • 緩沖區的容量是它包含的元素數量。 緩沖區的容量永遠不會為負,也不會改變。
  • 緩沖區的限制是不應讀取或寫入的第一個元素的索引。 緩沖區的限制永遠不會為負,也永遠不會大于緩沖區的容量。
  • 緩沖區的位置是下一個要讀取或寫入的元素的索引。 緩沖區的位置永遠不會為負,也不會大于其限制。

這是一個示例ByteBuffer的可視化示例,在示例中,ByteBuffer由字節數組支持,并且ByteBuffer的值是單詞“ test”(單擊以放大):

ByteByffer_example

該ByteBuffer等于(在equals()的意義上) 等于其在[ positionlimit )之間內容相同的任何其他ByteBuffer。

假設上面顯示的字節緩沖區是bb ,我們這樣做:

final ByteBuffer other = bb.duplicate();
other.position(bb.position() + 4);

現在,我們將有兩個ByteBuffer實例都引用相同的基礎字節數組,但是它們的內容將有所不同( 其他將為空):

ByteBuffer_after_duplicate_example

字節緩沖區的緩沖區/流對偶

有兩種訪問字節緩沖區內容的方法- 絕對訪問和相對訪問。 例如,假設我有一個ByteBuffer,我知道它包含兩個整數。 為了使用絕對定位提取整數,可以這樣做:

int first = bb.getInt(0)
int second = bb.getInt(4)

或者,可以使用相對定位提取它們:

int first = bb.getInt();
int second = bb.getInt();

第二種選擇通常很方便,但是以對緩沖區產生副作用 (即更改它)為代價。 不是內容本身,而是ByteBuffers視圖可以查看該內容。

這樣,如果將ByteBuffer用作流,則其行為類似于流。

最佳做法和陷阱

flip()緩沖區

如果要通過重復寫入來構建ByteBuffer,然后想將其贈送,則必須記住將它翻轉() 。 例如,這是一種將字節數組復制到ByteBuffer的方法,并假設使用默認編碼(請注意,此處使用的ByteBuffer.wrap()創建一個包裝指定字節數組的ByteBuffer,而不是復制其中的內容放入新的ByteBuffer中):

public static ByteBuffer fromByteArray(byte[] bytes) {final ByteBuffer ret = ByteBuffer.wrap(new byte[bytes.length]);ret.put(bytes);ret.flip();return ret;
}

如果我們不翻轉它,則返回的ByteBuffer 將為空,因為該位置等于limit

不要消耗緩沖區

除非特別打算這樣做,否則在讀取字節緩沖區時請注意不要“消耗”它。 例如,考慮采用默認編碼方式,將ByteBuffer轉換為String的此方法:

public static String toString(ByteBuffer bb) {final byte[] bytes = new byte[bb.remaining()];bb.duplicate().get(bytes);return new String(bytes);
}

不幸的是,沒有提供進行字節數組的絕對定位讀取的方法(但確實存在用于基元的絕對定位讀取)。

注意在讀取字節時使用了plicate() 。 如果我們不這樣做,該函數將對輸入ByteBuffer產生副作用 。 這樣做的代價是僅為了一次調用get()的目的就額外分配了一個新的ByteBuffer。 您可以在get()之前記錄ByteBuffer的位置,然后再將其還原,但這存在線程安全性問題(請參閱下一節)。

值得注意的是,這僅在您嘗試將ByteBuffer:s視為值時適用。 如果您正在編寫旨在對ByteBuffer產生副作用的代碼,將它們更像流一樣對待,那么您當然打算這樣做,并且本節不適用。

不要改變緩沖區

在不是特定于特定用例的通用代碼的情況下,(在我看來)對于執行(抽象)只讀操作(例如讀取字節緩沖區)的方法來說是一種好習慣。 ,不更改其輸入。 這是比“不要消耗ByteByffer”更強的要求。 以上一節中的示例為例,但嘗試避免額外分配ByteBuffer:

public static String toString(ByteBuffer bb) {final byte[] bytes = new byte[bb.remaining()];bb.mark();      // NOT RECOMMENDED, don't do thisbb.get(bytes);bb.reset();     // NOT RECOMMENDED, don't do thisreturn new String(bytes);
}

在這種情況下,我們在調用get()之前記錄ByteBuffer的狀態,然后再進行恢復(請參閱API文檔中的mark()reset() )。 這種方法有兩個問題。 第一個問題是上面的函數沒有組成 。 一個ByteBuffer僅具有一個“標記”,并且您的(非常通用,不具有上下文意識) toString()方法不能安全地假定調用者并未出于自身目的嘗試使用mark()和reset() 。 例如,假設以下調用者正在反序列化一個長度為前綴的字符串:

bb.mark();
int length = bb.getInt();
... sanity check length
final String str = ByteBufferUtils.toString(bb);
... do something
bb.reset(); // OOPS - reset() will now point 4 bytes off, because toString() modified the mark

(順便說一句,這是一個非常人為且奇怪的示例,因為我發現很難提出一個使用mark() / reset()的實際代碼示例,該代碼通常在處理流中的緩沖區時使用,像派系一樣,也感覺需要在所述緩沖區的其余部分上調用toString() 。我很想聽聽人們在這里提出了什么解決方案。例如,可以想象一個清晰的代碼庫中的策略在類似于toString()的面向值的上下文中允許mark() / reset() –但是即使您這樣做了(它可能會無意中違反了它的味道),您仍然會遭受后面提到的突變問題。)

讓我們看一下避免這種問題的toString()的替代版本:

public static String toString(ByteBuffer bb) {final byte[] bytes = new byte[bb.remaining()];bb.get(bytes);bb.position(bb.position() - bytes.length);     // NOT RECOMMENDED, don't do thisreturn new String(bytes);
}

在這種情況下,我們不修改標記,因此我們進行撰寫。 但是,我們仍然致力于改變輸入的“罪行”。 在多線程情況下,這是一個問題。 除非抽象隱含了該內容(例如,使用流或以類似流的方式使用ByteBuffer時),否則您不希望閱讀暗示其變化的內容。 如果要傳遞的ByteBuffer視為一個值,將其放入容器中,共享它們,等等–除非保證兩個線程永遠不會同時使用同一個ByteBuffer,否則對它們進行突變將引入細微的錯誤。 通常,此類錯誤的結果是奇怪的值損壞或意外的BufferOverFlowException:s。

不受此影響的版本出現在上面的“不要使用緩沖區”部分,該部分使用duplicate()構造一個臨時的ByteBuffer實例,可以在其上安全調用get()

compareTo()受字節簽名的約束

Java中的字節是有符號的 ,這與通常期望的相反。 但是,容易錯過的是,這也會影響ByteBuffer.compareTo() 。 該方法的Java API文檔顯示為:

“通過按字典順序比較剩余字節的序列來比較兩個字節緩沖區,而不考慮每個序列在其相應緩沖區中的開始位置。”

快速閱讀可能會使人相信結果通常是您期望的,但是當然,鑒于Java中字節的定義,情況并非如此。 結果是,包含最高位設置的值的字節緩沖區的順序將與您期望的有所不同。

Google出色的Guava庫具有UnsignedBytes幫助器 ,可減輕您的痛苦。

array()通常是使用錯誤的方法

通常,不要隨便使用array() 。 為了正確使用它,您要么必須知道字節緩沖區是數組支持的事實 ,要么必須使用 hasArray() 對其進行測試,并且在兩種情況下都有兩個單獨的代碼路徑。 此外,在使用它時, 必須使用arrayOffset()以確定ByteBuffer的第零個位置與字節數組相對應。

在典型的應用程序代碼中,除非您真的知道自己在做什么并且特別需要它,否則您將不會使用array() 。 也就是說,在某些情況下它很有用。 例如,假設您實現的是UnsignedBytes.compare()的ByteBuffer版本(同樣來自Guava )–您可能希望優化其中一個或兩個參數都支持數組的情況,以避免不必要的復制和頻繁調用。緩沖區。 對于這種通用且可能大量使用的方法,這種優化是有意義的。

參考: Java ByteBuffer –我們的JCG合作伙伴 Peter Schuller在(mod:world:scode)博客上的速成班 。

翻譯自: https://www.javacodegeeks.com/2012/12/the-java-bytebuffer-a-crash-course.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370133.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370133.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370133.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c語言cth三角函數表示,三角函數與雙曲函數基本公式對照表

圓函數(三角函數)1.基本性質:sin tan cos x x x ,cos cot sin xx x 1sec cos x x ,1csc sin x x tan cot 1x x sin csc 1x x sec cos 1x x 22sin cos 1x x 221tan sec x x ,221cot csc x x 2.奇偶性:sin()sin x x -- cos()cos x x - tan()tan x x --3.…

實現編輯功能有哪幾個action_Web 應用的撤銷重做實現

背景前不久,我參與開發了團隊中的一個 web 應用,其中的一個頁面操作如下圖所示:GIF這個制作間頁面有著類似 PPT 的交互:從左側的工具欄中選擇元素放入中間的畫布、在畫布中可以刪除、操作(拖動、縮放、旋轉等&#xff…

為什么我們要做三份 Webpack 配置文件

時至今日,Webpack 已經成為前端工程必備的基礎工具之一,不僅被廣泛用于前端工程發布前的打包,還在開發中擔當本地前端資源服務器(assets server)、模塊熱更新(hot module replacement)、API Pro…

使用maven插件構建docker鏡像

為什么要用插件 主要還是自動化的考慮,如果額外使用Dockerfile進行鏡像生成,可能會需要自己手動指定jar/war位置,并且打包和生成鏡像間不同步,帶來很多瑣碎的工作。 插件選擇 使用比較多的是spotify的插件:https://github.com/spo…

windows下如何安裝pip以及如何查看pip是否已經安裝成功?

最近剛學習python,發現很多關于安裝以及查看pip是否安裝成的例子都比較老,不太適合于現在(python 3.6 )因此,下一個入門級別的教程。 0:首先如何安裝python我就不做介紹了。 1:如果安裝的是pyth…

檢查用戶顯示器的分辨率

檢查用戶顯示器的分辨率 轉載于:https://www.cnblogs.com/Renyi-Fan/p/8088012.html

android 字體 dpi,詳解Android開發中常用的 DPI / DP / SP

Android的碎片化已經被噴了好多年,隨著國內手機廠商的崛起,碎片化也越來越嚴重,根據OpenSignal的最新調查,2014年市面上有18796種不同的Android設備,作為開發者,一個無法回避的難題就是需要適配各種各樣奇奇…

android studio閃退代碼不報錯_代碼不報錯,不代表真的沒錯

今天是生信星球陪你的第695天大神一句話,菜鳥跑半年。我不是大神,但我可以縮短你走彎路的半年~就像歌兒唱的那樣,如果你不知道該往哪兒走,就留在這學點生信好不好~這里有豆豆和花花的學習歷程,從新手到進階&#xff0c…

Centos7操作系統部署指南

一、硬件環境: Dell R620 二、軟件環境: Centos6.4 X86_64 KVM Windows7vnc 三、安裝說明 操作系統更新之迅速,讓作為新手的系統運維人員有點措手不及,相對于老手就胸有成竹。怎么講?由于老手對技術方向把握的非常好&…

Eclipse插件中的SLF4J登錄

一直都在使用Maven和純Java庫進行開發,我從沒想過在開發Eclipse插件時發出一些日志語句可能會成為問題。 但是,在Eclipse開發人員的想象中,一切似乎總是在Eclipse環境中,而Eclipse宇宙之外則什么都沒有。 如果您使用Google搜索上…

CSS(四)

css元素溢出 當子元素的尺寸超過父元素的尺寸時,需要設置父元素顯示溢出的子元素的方式,設置的方法是通過overflow屬性來設置。 overflow的設置項: 1、visible 默認值。內容不會被修剪,會呈現在元素框之外。2、hidden 內容會被修…

mysql排名

轉載自思心思危http://www.cnblogs.com/zengguowang/p/5541431.html 一、sql1{不管數據相同與否,排名依次排序(1,2,3,4,5,6,7.....)} SELECTobj.user_id,   obj.score,  rownum : rownum 1 AS rownum FROM(SELECT…

python中變量名后的逗號_深入淺析python變量加逗號,的含義

逗號,用于生成一個長度為1的元組>>> (1)1>>> (1,)(1,)>>> 1,(1,)因此需要將長度為1的元組中元素提取出來可以用,簡化賦值操作>>> a(1,)>>> ba>>> b(1,)>>> b,a>>> b1最后print打印變量加,實現連續打印…

廣告的顯示和關閉

app或游戲的主頁顯示廣告頁面,實現方式: public class MainActivity extends Activity implements View.OnClickListener{private Button btnShowAd;private RelativeLayout layoutAd;Overrideprotected void onCreate(Bundle savedInstanceState) {supe…

android簽到功能模塊,基于android的課堂簽到系統.doc

基于android的課堂簽到系統本科畢業論文(設計)題 目 基于Android的課堂簽到系統學生姓名 XXX指導教師 XX學 院 信息科學與工程學院專業班級 計算機科學與技術0908班完成時間 2013年5月 摘 要在大學課堂中,簽到問題一直困擾著老師和同學們。傳統課堂簽到的手段大多是…

Java EE 7社區調查結果!

在JSR 342下可以繼續進行Java EE 7的工作。一切進展順利,Java EE 7現在處于“初稿審查”階段。 在11月初, Oracle發布了一個有關即將推出的Java EE 7功能的小型社區調查 。 昨天結果公布了。 超過1,100名開發人員參加了調查,并且幾乎對每個問…

CSS(三)

CSS盒子模型 盒子模型解釋 元素在頁面中顯示成一個方塊,類似一個盒子,CSS盒子模型就是使用現實中盒子來做比喻,幫助我們設置元素對應的樣式。盒子模型示意圖如下: 把元素叫做盒子,設置對應的樣式分別為:盒…

一道關于運行順序題

function foo(){   getName function(){console.log(1)}   return this } foo.getName function(){console.log(2)} foo.prototype.getName function(){console.log(3)} var getName function(){console.log(4)} function getName(){console.log(5)} foo.getName()//2 …

android+小米文件管理器源碼,小米開源文件管理器MiCodeFileExplorer-源碼研究(2)-2個單實例工具類...

從本篇開始,講解net.micode.fileexplorer.util工具包中的類。這個包下的類,功能也比較單一和獨立。很多代碼的思想和實現,可以用于JavaWeb和Android等多種環境中。一、單實例活動管理器ActivitiesManager一個單實例的活動管理器,從…

移動優先的響應式布局

前面的話 隨著移動互聯網的興起,不同設備的分辨率相差較大,如果在不同的設置上顯示同一個頁面,則用戶體驗差。響應式網頁設計是一種方法,使得一個網站能夠兼容多個終端,而不用為每個終端制作特定的版本。它使得一個網站…