输入流输出流是以内存为标准_构建用于测试的超大内存输入流
輸入流輸出流是以內存為標準
由于某種原因,我需要非常大的,甚至可能是無限的InputStream ,它會反復地反復返回相同的byte[] 。 這樣,我可以通過重復小樣本來產生瘋狂的大數據流。 可以在Guava中找到類似的功能: Iterable<T> Iterables.cycle(Iterable<T>)和Iterator<T> Iterators.cycle(Iterator<T>) 。 例如,如果您需要0和1的無限來源,只需說出Iterables.cycle(0, 1)并無限獲取Iterables.cycle(0, 1) 0, 1, 0, 1, 0, 1... 不幸的是,我還沒有為InputStream找到這樣的工具,所以我跳入自己編寫的工具。 本文記錄了我在此過程中犯的許多錯誤,主要是由于過于復雜和過度設計的簡單解決方案。
我們實際上并不需要無限的InputStream ,能夠創建非常大的InputStream (例如32 GiB)就足夠了。 因此,我們采用以下方法:
它基本上采用字節sample數組,并返回一個InputStream返回這些字節。 但是,當sample用完時,它將翻轉,再次返回相同的字節-重復此過程指定的次數,直到InputStream信號結束。 我尚未真正嘗試過但似乎最顯而易見的一種解決方案:
public static InputStream repeat(byte[] sample, int times) {final byte[] allBytes = new byte[sample.length * times];for (int i = 0; i < times; i++) {System.arraycopy(sample, 0, allBytes, i * sample.length, sample.length);}return new ByteArrayInputStream(allBytes); }我看到你在那笑! 如果sample是100個字節,并且我們需要32 GiB的輸入重復這100個字節,則生成的InputStream不應真正分配32 GiB的內存,我們在這里必須更加聰明。 實際上,上面的repeat()有另一個細微的錯誤。 Java中的數組限制為2 31 -1個條目( int ),比其高32 GiB。 該程序編譯的原因是此處無聲的整數溢出: sample.length * times 。 此乘法不適用于int 。
好的,讓我們嘗試至少在理論上可行的方法。 我的第一個想法是:如果我創建多個ByteArrayInputStream共享相同的byte[] sample (它們不進行急切的復制),然后以某種方式將它們連接在一起怎么辦? 因此,我需要一些InputStream適配器,該適配器可以采用任意數量的基礎InputStream并將它們鏈接在一起-當第一個流耗盡時,切換到下一個。 當您在Apache Commons或Guava中尋找某些東西,并且顯然永遠在JDK中時,這一尷尬時刻… java.io.SequenceInputStream幾乎是理想的選擇。 但是,它只能精確地鏈接兩個基礎InputStream 。 當然,由于SequenceInputStream本身就是InputStream ,我們可以遞歸地將其用作外部SequenceInputStream的參數。 重復此過程,我們可以將任意數量的ByteArrayInputStream在一起:
public static InputStream repeat(byte[] sample, int times) {if (times <= 1) {return new ByteArrayInputStream(sample);} else {return new SequenceInputStream(new ByteArrayInputStream(sample),repeat(sample, times - 1));} }如果times為1,則將sample包裝在ByteArrayInputStream 。 否則,遞歸使用SequenceInputStream 。 我認為您可以立即發現此代碼的問題:太深的遞歸。 嵌套級別與times參數相同,將達到數百萬甚至數十億。 肯定有更好的辦法。 幸運的是,較小的改進將遞歸深度從O(n)更改為O(logn):
public static InputStream repeat(byte[] sample, int times) {if (times <= 1) {return new ByteArrayInputStream(sample);} else {return new SequenceInputStream(repeat(sample, times / 2),repeat(sample, times - times / 2));} }老實說,這是我嘗試的第一個實現。 這是分而治之原理的簡單應用 ,在這里我們將結果平均分成兩個較小的子問題。 看起來很聰明,但是有一個問題:容易證明我們創建了t( t =times ) ByteArrayInputStreams和O(t) SequenceInputStream 。 共享sample字節數組時,數百萬個各種InputStream實例浪費了內存。 這導致我們可以選擇實現,無論times多少, InputStream創建一個InputStream :
import com.google.common.collect.Iterators; import org.apache.commons.lang3.ArrayUtils;public static InputStream repeat(byte[] sample, int times) {final Byte[] objArray = ArrayUtils.toObject(sample);final Iterator<Byte> infinite = Iterators.cycle(objArray);final Iterator<Byte> limited = Iterators.limit(infinite, sample.length * times);return new InputStream() {@Overridepublic int read() throws IOException {return limited.hasNext() ?limited.next() & 0xFF :-1;}}; }畢竟,我們將使用Iterators.cycle() 。 但是在我們必須將byte[]轉換為Byte[]因為迭代器只能與objets一起使用,而不能與原語一起使用。 沒有慣用的方法ArrayUtils.toObject(byte[])語數組轉換為盒裝類型數組,因此我使用來自Apache Commons Lang的ArrayUtils.toObject(byte[]) 。 有了對象數組,我們可以創建一個infinite迭代器,循環遍歷sample值。 由于我們不需要無限的流,因此再次使用了來自Guava的Iterators.limit(Iterator<T>, int)來切斷無限迭代Iterators.limit(Iterator<T>, int) 。 現在,我們只需要從Iterator<Byte>到InputStream進行橋接–畢竟它們在語義上代表同一件事。
該解決方案遭受兩個問題。 首先,由于拆箱,它會產生大量垃圾。 垃圾回收并不太關心死的,短命的物品,但看起來仍然很浪費。 我們之前已經遇到的第二個問題: sample.length * times乘以倍數可能會導致整數溢出。 由于Iterators.limit()占用int不long ,因此沒有固定的理由,因此無法修復。 順便說一句,我們通過按位和 0xFF避免了第三個問題-否則,值為-1 byte將表示流的結束,事實并非如此。 x & 0xFF已正確轉換為無符號255 ( int )。
因此,即使上面的實現是簡短而甜美,聲明式而不是命令式的,但它仍然太慢且受限制。 如果您有C背景,我可以想象您看到我掙扎時會感到多么不舒服。 在我最后想到的是最簡單,痛苦的簡單和低級的實現之后:
public static InputStream repeat(byte[] sample, int times) {return new InputStream() {private long pos = 0;private final long total = (long)sample.length * times;public int read() throws IOException {return pos < total ?sample[(int)(pos++ % sample.length)] :-1;}}; }無GC的純JDK,快速,簡單易懂。 讓這成為您的一個教訓:從您想到的最簡單的解決方案開始,不要過度設計并且不要太聰明。 我以前的解決方案(聲明性,功能性,不變性等)–也許它們看起來很聰明,但是它們既不快速也不容易理解。
我們剛剛開發的實用程序不僅是一個玩具項目,還將在后續文章中使用 。
翻譯自: https://www.javacodegeeks.com/2014/07/building-extremely-large-in-memory-inputstream-for-testing-purposes.html
輸入流輸出流是以內存為標準
總結
以上是生活随笔為你收集整理的输入流输出流是以内存为标准_构建用于测试的超大内存输入流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 两点之间什么最短 两点之间最短的是什么
- 下一篇: 带有Prometheus的弹簧靴和千分尺