Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在某些情况下,Java Stream.forEach方法比其他循环更快?_Java_Loops_For Loop_Java Stream_Jmh - Fatal编程技术网

为什么在某些情况下,Java Stream.forEach方法比其他循环更快?

为什么在某些情况下,Java Stream.forEach方法比其他循环更快?,java,loops,for-loop,java-stream,jmh,Java,Loops,For Loop,Java Stream,Jmh,我目前正在进行一个项目,在该项目中,我使用Java微基准线束(JMH)框架测量Java中不同类型循环的速度。我得到了一些关于流的有趣结果,但我无法解释,我想知道是否有更了解流和数组列表的人可以帮助我解释我的结果 基本上,当遍历大小约为100的数组列表时,stream.forEach方法比任何其他类型的循环快得多: 我的结果图表如下所示: 我尝试过使用对象和字符串的数组列表,结果都很相似。随着列表的大小越来越大,stream方法的速度会更快,但其他列表之间的性能差距会越来越小。我不知道是什么导致

我目前正在进行一个项目,在该项目中,我使用Java微基准线束(JMH)框架测量Java中不同类型循环的速度。我得到了一些关于流的有趣结果,但我无法解释,我想知道是否有更了解流和数组列表的人可以帮助我解释我的结果

基本上,当遍历大小约为100的数组列表时,stream.forEach方法比任何其他类型的循环快得多:

我的结果图表如下所示:

我尝试过使用对象和字符串的数组列表,结果都很相似。随着列表的大小越来越大,stream方法的速度会更快,但其他列表之间的性能差距会越来越小。我不知道是什么导致了这些结果

@Fork(5)
@基准模式(模式平均时间)
@输出时间单位(时间单位纳秒)
@预热(迭代次数=10,时间=100,时间单位=timeUnit.ms)
@测量(迭代次数=10,时间=100,时间单位=时间单位。毫秒)
@状态(Scope.Thread)
公共类StackOverflowQ{
列表intList;
int size=100;
@设置
公共作废设置(){
intList=新阵列列表(大小);

对于(inti=0;i这个答案谈论的是
java.util.ArrayList
。其他
集合
实现可能(也确实如此!)显示完全不同的结果

简而言之:因为流使用
拆分器
而不是
迭代器

说明: 基于
Iterator
的迭代基于
hasNext
next
方法调用,第二个方法调用变异了
Iterator
实例。这会带来一些性能成本。
Spliterator
支持批量迭代。增强的for循环(
for(te:iterable){…}
)似乎使用了
迭代器

性能通常应该是次要考虑因素;您应该使用最能描述您的意图的结构。虽然由于向后兼容的原因,这将很困难,但基于拆分器的forEach和增强的for循环(在
ArrayList
上)之间的性能差异可能在将来消失

为循环建立索引怎么样?(
List#get(int)

它们的性能比拆分器差,部分原因是它们需要验证索引。其他原因可能包括方法调用,例如在索引处获取数据,而
spliterator
直接访问数组。但这纯粹是推测

微型JMH基准 下面你可以看到一个微小的基准,它证实了上述理论。请注意,最好的基准应该运行更长的时间

@State(Scope.Benchmark)
@Fork(值=2)
@预热(迭代次数=2次,时间=3次)
@测量(迭代次数=2次,时间=3次)
公共A类{
公开名单;
@设置
公共作废设置(){
列表=新的ArrayList();
对于(inti=0;i<1000;i++)列表,添加(i);
}
@基准
公共void collectionForEach(黑洞){
list.forEach(洞::消耗);
}
@基准
公共无效迭代器(黑洞){
for(Iterator Iterator=list.Iterator();Iterator.hasNext();){
消费(迭代器.next());
}
}
@基准
公共空间增强(黑洞){
用于(对象e:列表){
洞。消耗(e);
}
}
@基准
公共无效迭代器foreach(黑洞){
list.iterator().forEachRemaining(hole::consume);
}
@基准
公共空隙指数(黑洞){
for(int i=0,size=list.size();i
结果是,“ops/s”表示每秒操作数,越高越好

Benchmark              Mode  Cnt       Score      Error  Units
A.collectionForEach   thrpt    4  158047.064 ±  959.534  ops/s
A.iteratorFor         thrpt    4  177338.462 ± 3245.129  ops/s
A.enhancedFor         thrpt    4  177508.037 ± 1629.494  ops/s
A.iteratorForEach     thrpt    4  184919.605 ± 1922.114  ops/s
A.indexedFor          thrpt    4  193318.529 ± 2715.611  ops/s
A.streamForEach       thrpt    4  280963.272 ± 2253.621  ops/s
A.spliteratorForEach  thrpt    4  283264.539 ± 3055.967  ops/s

您是否尝试从循环中提取
size()
调用(使用
size
而不是
intList.size()
)-检查这个问题,它讨论了流的优点和缺点,这可能会有所帮助-您能确认流是
sequential()吗
?为了正确起见,我将在您的基准测试中包含此显式调用。您还应该与
For(Integer I:intList)bh.consume(doWork(I));
变量进行比较。此外,您可以在
数组列表上直接使用
forEach
,而不使用流。两个方法如何给出四个结果?