与"Byte array of unknown length in java"类似,我需要能够将未知数量的字节从数据源写入byte[]数组。* 然而,* 对于压缩算法,我需要能够读取之前存储的字节,因此ByteArrayOutputStream不适合我。
现在我有一个方案,我分配固定大小N的ByteBuffers,当我达到N,2N,3N字节等时添加一个新的。在数据耗尽后,我将所有缓冲区转储到一个现在已知大小的数组中。
有更好的方法吗?固定大小的缓冲区会降低压缩算法的灵活性。
与"Byte array of unknown length in java"类似,我需要能够将未知数量的字节从数据源写入byte[]数组。* 然而,* 对于压缩算法,我需要能够读取之前存储的字节,因此ByteArrayOutputStream不适合我。
现在我有一个方案,我分配固定大小N的ByteBuffers,当我达到N,2N,3N字节等时添加一个新的。在数据耗尽后,我将所有缓冲区转储到一个现在已知大小的数组中。
有更好的方法吗?固定大小的缓冲区会降低压缩算法的灵活性。
7条答案
按热度按时间5anewei61#
使用循环字节缓冲区怎么样?它有可能动态增长,而且效率很高。
这里有一个实现:http://ostermiller.org/utils/CircularByteBuffer.java.html
yhived7q2#
为什么不子类化
ByteArrayOutputStream
呢?这样子类就可以访问受保护的buf
和count
字段,并且可以向类中添加方法来直接操作它们。gmol16393#
ByteArrayOutputStream的代价是底层数组的大小调整。您的固定块例程消除了其中的大部分。如果调整大小的代价对您来说还不够大(例如,在您的测试中,ByteArrayOutputStream“足够快”,并且不提供撤销内存压力),那么也许像Vanza建议的那样,将ByteArrayOutputStream子类化会对您有用。
我不知道你的压缩算法,所以我不能说为什么你的块列表会降低它的灵活性,或者为什么压缩算法甚至会知道这些块。但是因为块可以是动态的,你可以适当地调整块的大小,以更好地支持你正在使用的各种压缩算法。
如果压缩算法可以在“流”(即固定大小的数据块)上工作,那么块大小就应该很重要,因为你可以在实现中隐藏所有这些细节。完美的情况是,如果压缩算法希望它的数据块与你分配的块大小相匹配,这样你就不必复制数据来供给压缩器。
rfbsl7qr4#
虽然你当然可以使用ArrayList来实现这个目的,但是你会看到4- 8倍的内存开销--假设字节不是新分配的,而是共享一个全局示例(因为这对整数是真的,我假设它对字节也有效)--而且你会失去所有的缓存局部性。
因此,虽然您可以子类化ByteArrayOutputStream,但即使这样也会产生开销(方法是同步的),所以我个人会推出我自己的类,当你写它的时候,它会动态增长。比你现在的方法效率低,但是很简单,我们都知道摊余成本的部分-否则你显然也可以使用你的解决方案。只要您将解决方案 Package 在一个干净的界面中,您就可以隐藏复杂性并仍然获得良好的性能
或者说:不,你几乎不能比你已经在做的更有效地做这件事,每个内置的java集合应该因为这样或那样的原因表现更差。
slmsl1lt5#
正如Chris回答的那样,CircularByteBuffer api是一条可行之路。幸运的是,它现在在中央maven repo中。引用this link的一个片段,它简单如下:
循环缓冲区的单线程示例
优点是:
最后,我们摆脱了内存问题和管道API
2nbm6dog6#
为简单起见,可以考虑使用
java.util.ArrayList
:Java1.5和更高版本将提供
byte
和Byte
类型之间的自动装箱和拆箱。性能可能比ByteArrayOutputStream
略差,但易于阅读和理解。py49o6xq7#
最后我写了自己的方法,使用一个临时的固定缓冲区数组,并在每次填充固定缓冲区后将其追加到最终字节数组中。它将继续覆盖固定缓冲区数组并追加到最终数组中,直到所有字节都被读取和复制。最后,如果temporaryArray未填充,它会将读取的字节从那个数组复制到最后一个数组。我的代码是为Android编写的,所以你可能需要使用类似于
ArrayUtils.concatByteArrays
(com.google.gms.common.util.ArrayUtils)的方法我的代码将临时数组的大小设置为100(growBufferSize),但最好设置为500或1000以上,或者在您使用的环境中性能最佳的值。最终结果将存储在bytesfinal数组中。
此方法应减少内存使用以防止OutOfMemoryError。由于它主要使用基元,因此应减少内存。