构建用于测试的超大型内存InputStream

由于某种原因,我需要非常大的,甚至可能是无限的InputStream ,它会反复地反复返回相同的byte[] 。 这样,我可以通过重复小样本来产生大量的数据流。 在Guava中可以找到类似的功能: Iterable<T> Iterables.cycle(Iterable<T>)Iterator<T> Iterators.cycle(Iterator<T>) 。 例如,如果您需要01的无限来源,只需说出Iterables.cycle(0, 1)并无限获取Iterables.cycle(0, 1) 0, 1, 0, 1, 0, 1... 不幸的是,我还没有为InputStream找到这样的工具,所以我跳入自己编写的工具。 本文记录了我在此过程中犯的许多错误,主要是由于过于复杂和过度设计的简单解决方案。


我们实际上不需要无限的InputStream ,能够创建非常大的InputStream (例如32 GiB)就足够了。 因此,我们采用以下方法:

public static InputStream repeat(byte[] sample, int times)

它基本上采用字节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 =timesByteArrayInputStreams和O(t) SequenceInputStream 。 共享sample字节数组时,数百万个各种InputStream实例浪费了内存。 这使我们另一种实现,创建只有一个InputStream ,无论价值times

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()占用intlong ,因此没有固定的理由,因此无法修复。 顺便说一句,我们通过做按位避免第三个问题0xFF -否则, byte值为-1将信号流的结束,这是情况并非如此。 x & 0xFF被正确转换为无符号255int )。

因此,即使上面的实现是简短而甜美,声明式而不是命令式的,但它仍然太慢且受限制。 如果您具有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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 调试小经验

1、查看netlink socket 丢包 cat /proc/net/netlink sk Eth Pid Groups Rmem Wmem Dump Locks Drops Inode c91eda00 0 1172 00000001 0 0 00000000 2 0 27767 c43eee00 0 -4099 00000000 0 0 00000000 2 0 41200 c9266e00 0 1186 00000000 0 0 00000000 2 0 28922 c1af580…

前端打印功能

方法一&#xff1a; html代码&#xff1a; 在要打印的内容上加入&#xff1a;<!--startprint-->和<!--endprint--> js代码&#xff1a; $(.printData).click(function(){bdhtmlwindow.document.body.innerHTML; sprnstr"<!--startprint-->"; …

Vlc支持IE 360 低版本的Google浏览器

VLC 插件代码&#xff1a; <object typeapplication/x-vlc-plugin pluginspage"http://www.videolan.org/" idvlc eventsfalse width"720" height"410"><param namemrl valuertsp://admin:123456******:554/h264/ch1/main/av_stream /&…

同步多线程

同步多线程&#xff08;SMT&#xff09;是一种在一个CPU 的时钟周期内能够执行来自多个线程的指令的硬件多线程技术。本质上&#xff0c;同步多线程是一种将线程级并行处理&#xff08;多CPU&#xff09;转化为指令级并行处理&#xff08;同一CPU&#xff09;的方法。 同步多线…

如何使用布隆过滤器在Java中建立大容量的内存缓存

背景 缓存是解决日常软件问题的重要概念。 您的应用程序可能会执行CPU密集型操作&#xff0c;而您又不想一次又一次地执行这些操作&#xff0c;而是只导出一次结果并将其缓存在内存中。 有时瓶颈是IO&#xff0c;例如您不想重复访问数据库&#xff0c;并且想缓存结果并仅在基础…

Centos7安装Python3的方法

由于centos7原本就安装了Python2&#xff0c;而且这个Python2不能被删除&#xff0c;因为有很多系统命令&#xff0c;比如yum都要用到。 [rootVM_105_217_centos Python-3.6.2]# python Python 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)…

ajax请求导致status为canceled的原因

在使用layui的form表单提交以后&#xff0c;请求状态总是canceled。后来在form表单的后面添加了一行代码&#xff1a; return false; 就可以了。 文档&#xff1a;https://www.layui.com/doc/modules/form.html#onsubmit 错误&#xff1a; 解决方法&#xff1a; 总结一下&…

CSS图片水平垂直居中

Html: <div id"MainContent"><img src"QQ截图20150515140512.png" width"247" height"192"></img></div></div> CSS: #MainContent {display:table-cell;width: 700px;height: 800px;background: #0C78…

如何开启OpenSSL和mcrypt

1&#xff0c;要想开启mcrypt支持&#xff0c;系统需要安装了libmcrypt.dll库。这个一般用户是没有安装过的&#xff0c;但不用担心&#xff0c;PHP的windows发行包里已经给我们附带了此文件&#xff0c;在PHP压缩包的根目录下即可找到&#xff0c;然后将其复制到%system%/syst…

如果BigDecimal是答案,那肯定是一个奇怪的问题

总览 许多开发人员已确定BigDecimal是处理金钱的唯一方法。 通常&#xff0c;他们通过用BigDecimal替换double来确定错误或十个错误。 我对此没有说服力的是&#xff0c;也许他们可以解决double处理中的错误以及使用BigDecimal的额外开销。 根据我的比较&#xff0c;当被问到改…

算法思想_常见四种

文章目录常用算法思想描述穷举算法思想1. 基本思想2. 步骤3.实例递推算法1. 基本思想2. 步骤3. 实例递归算法1. 基本思想2. 步骤3. 实例1. 分治算法1. 基本思想2. 步骤3. 实例概率算法1. 基本思想2. 步骤3. 实例如果还有时间&#xff0c;将继续更新常用算法思想描述 穷举算法思…

[转] 做个自强不息的青年

人的出生和际遇本身造就了每个人的道路都不同&#xff0c;对于青年人来说&#xff0c;如何能够面对逆顺&#xff0c;如何面对起伏&#xff0c;如何能够正确设定理想并为之激发生命的火焰&#xff0c;在持续的奋斗中不断提升自我&#xff0c;自强不息而自立利他&#xff1f;在与…

commonJS — 全局操作(for Window)

for Window github: https://github.com/laixiangran/commonJS/blob/master/src/forWindow.js 代码 /*** Created by laixiangran on 2016/1/24* homepage&#xff1a;http://www.cnblogs.com/laixiangran/* for Window*/ (function() {var com window.COM window.COM || {}…

点击返回上一页失效

点击取消的时候&#xff0c;返回上一页&#xff0c;没有效果&#xff0c;仍然停留在当前页&#xff0c;还报500&#xff1b;改成window.history.go(-1)也不生效 代码&#xff1a; <button class"st-btn-normal st-btn-cancel cancle" onclick"javascript:wi…

如何通过7个Logback调整立即改善Java日志记录

基准测试可帮助您发现Logback在压力下的性能 日志记录对于服务器端应用程序是必不可少的&#xff0c;但这是有代价的。 令人惊讶的是&#xff0c;微小的更改和配置调整对应用程序的日志记录吞吐量有多大影响。 在这篇文章中&#xff0c;我们将根据每分钟的日志条目对Logback的性…

JS中的编码

今天发现我输入的中文变成了另外一种格式&#xff0c;查了一下&#xff0c;原来是转换成了数字编码。在这里介绍一下数字编码和base64&#xff0c;做个记录 1.出现原因&#xff1a;在开发中经常需要对用户输入的数据进行编码然后才能通过HTTP请求发送给后台&#xff0c;或者对…

commonJS — DOM操作(for DOM)

for DOM github: https://github.com/laixiangran/commonJS/blob/master/src/forDOM.js 代码 /*** Created by laixiangran on 2016/1/24* homepage&#xff1a;http://www.cnblogs.com/laixiangran/* for DOM*/(function(undefined) {var com window.COM window.COM || {};…

deMeer5_Attacker

SoccerCommand Player::deMeer5_Attacker(){SoccerCommand soc(CMD_ILLEGAL);if (WM->isBeforeKickOff()){if (formations->getFormation() ! FT_INITIAL || //不在开球的阵型 WM->getAgentGlobalPosition().getDistanceTo(WM->getStrategicPosition()) > 2.…

【Flutter】解决依赖版本冲突

为什么会存在版本依赖问题 我们在开发flutter项目中&#xff0c;常常会依赖第三方库。flutter依赖项越多&#xff0c;就越有可能出现依赖版本冲突。 Because new_trend depends on build_runner >0.9.0 which requires SDK version >2.0.0-dev.61 <3.0.0, version sol…

安装vue-cli时报错

今天用npm安装vue-cli时一直报错&#xff0c;后面百度说用cnpm重装&#xff0c;结果还是报错&#xff0c;后面找到一个博主的&#xff0c;说是vue升级的原因&#xff0c;vue init webpack hello-world 已经替换为 vue create hello-world npm install -g vue/cli-init # vue…