簡體   English   中英

Java 中未知長度的字節數組:第二部分

[英]Byte array of unknown length in Java: Part II

類似於“Java 中未知長度的字節數組”,我需要能夠將未知數量的字節從數據源寫入字節 [] 數組。 但是,我需要能夠從較早存儲的字節中讀取壓縮算法,因此ByteArrayOutputStream對我不起作用。

現在我有一個分配固定大小 N 的 ByteBuffers 的方案,當我達到 N、2N、3N 字節等時添加一個新的。數據用完后,我將所有緩沖區轉儲到一個現在已知大小的數組中。

有一個更好的方法嗎? 具有固定大小的緩沖區會降低壓縮算法的靈活性。

使用循環字節緩沖區怎么辦? 它有可能動態增長並且高效。

這里有一個實現: http : //ostermiller.org/utils/CircularByteBuffer.java.html

為什么不將ByteArrayOutputStream子類化? 這樣,您的子類就可以訪問受保護的bufcount字段,並且可以向類中添加方法以直接對其進行操作。

正如克里斯回答的那樣, CircularByteBuffer API是必經之路。 幸運的是,它現在位於中央行家倉庫中。 從此鏈接引用一個片段,它很簡單,如下所示:

循環緩沖區的單線程示例

// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());

優點是:

  • 一個CircularBuffer類,而不是兩個管道類。
  • 在“緩沖所有數據”和“額外線程”方法之間進行轉換更容易。
  • 您可以更改緩沖區大小,而不是依賴管道中硬編碼的1k緩沖區。

最后,我們擺脫了內存問題和管道API的困擾

ByteArrayOutputStream的開銷是調整基礎數組的大小。 您的固定程序塊消除了很多麻煩。 如果調整大小對您來說並不重要(例如,在測試ByteArrayOutputStream時“足夠快”,並且不提供撤消內存壓力),那么按照vanza的建議,將ByteArrayOutputStream子類化將對您有用。

我不知道您的壓縮算法,所以我不能說為什么您的塊列表使靈活性降低,或者甚至為什么壓縮算法甚至會知道這些塊。 但是由於塊可以動態變化,因此您可以適當地調整塊大小,以更好地支持您使用的各種壓縮算法。

如果壓縮算法可以在“流”(即固定大小的數據塊)上工作,則塊大小應該很重要,因為您可以從實現中隱藏所有這些細節。 理想的情況是,如果壓縮算法希望以與您分配的塊的大小匹配的塊為單位的數據,那么您就不必復制數據來饋送壓縮器。

雖然您當然可以為此使用ArrayList,但是您幾乎可以看到4到8倍的內存開銷-假設字節不是新分配的,而是共享一個全局實例(因為對於整數,這是正確的,所以我認為它適用於Bytes為很好)-您將丟失所有緩存位置。

因此,雖然您可以繼承ByteArrayOutputStream的子類,但是即使在那兒,您也會得到不需要的開銷(方法已同步)。 因此,我個人將推出自己的類,該類在您編寫時會動態增長。 效率不如您當前的方法,但很簡單,而且我們都知道該零件的攤銷成本-否則您顯然也可以使用您的解決方案。 只要將解決方案包裝在一個干凈的界面中,您就可以隱藏復雜性並仍然獲得良好的性能

或以其他方式說:不,您幾乎不能比已經做的事更有效率,並且每個內置的Java Collection都應出於一個或另一個原因而表現較差。

為簡單起見,您可以考慮使用java.util.ArrayList

ArrayList<Byte> a = new ArrayList<Byte>();
a.add(value1);
a.add(value2);
...
byte value = a.get(0);

Java 1.5及更高版本將在byteByte類型之間提供自動裝箱和拆箱。 性能可能比ByteArrayOutputStream 稍差 ,但易於閱讀和理解。

我最終編寫了自己的方法,該方法使用臨時固定緩沖區數組,並在固定緩沖區填滿后每次將其附加到最終字節數組。 它將繼續覆蓋固定緩沖區數組並附加到最終數組,直到讀取並復制所有字節。 最后,如果 temporaryArray 沒有被填充,它會將讀取的字節從該數組復制到最終數組中。 我的代碼是為 Android 編寫的,因此您可能需要使用與ArrayUtils.concatByteArrays (com.google.gms.common.util.ArrayUtils)類似的方法

我的代碼將臨時數組大小設置為 100 ( growBufferSize ),但最好設置為高於 500 甚至 1000 或任何在您使用的環境中表現最佳的值。 最終結果將存儲在bytesfinal數組中。

此方法應減少內存使用量以防止OutOfMemoryError s。 由於它主要使用基元,因此應該減少內存。

final int growBufferSize = 100;
byte[] fixedBuffer = new byte[growBufferSize];
byte[] bytesfinal = new byte[0];

int fixedBufferIndex=0;
while (zin.available()>0){
    fixedBuffer[fixedBufferIndex] = (byte)zin.read();
    if (fixedBufferIndex == growBufferSize-1){
        bytesfinal = ArrayUtils.concatByteArrays(bytesfinal,fixedBuffer);
        fixedBufferIndex = -1;
    }

    fixedBufferIndex++;
}

if (fixedBufferIndex!=0){
    byte[] lastBytes = new byte[fixedBufferIndex];
    //copy last read bytes to new array
    for (int i = 0; i<fixedBufferIndex; i++){
        lastBytes[i]=fixedBuffer[i];
    }

    //add last bits of data
    bytesfinal = ArrayUtils.concatByteArrays(bytesfinal,lastBytes);
    lastBytes = null;
    fixedBuffer = null;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM