繁体   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