Stream#limit可以返回比预期更少的元素吗?

时间:2015-01-22 17:11:18

标签: java java-8 java-stream

如果下面的流s至少有n个元素,那么流sLimit可能包含少于n个元素的情况是什么?

Stream sLimit = s.limit(n);

问题的原因:在this answer中,我读到了:

  

尽管有外观,但使用limit(10)并不一定会产生一个只包含10个元素的SIZED流 - 它可能会有更少的内容。

2 个答案:

答案 0 :(得分:6)

你误解了这句话。如果Stream至少包含n个元素并且您在其上调用limit(n),那么它将具有nStream元素,但Stream 实现可能没有意识到它,因此性能不佳。

相比之下,某些Spliterator来源(IntStream s)肯定知道它们具有固定的大小,例如通过Stream for an array创建IntStream.rangeStream时。它们的优化程度优于limit(n) parallel

当您通过Stream创建Stream.generate(MyClass::new).limit(10) IntStream.range(0, n).mapToObj(i -> new MyClass())时,仍会按顺序调用构造函数,并且只能并行执行后续操作。相反,使用Stream时,整个{{1}}操作(包括构造函数调用)可以并行运行。

答案 1 :(得分:5)

我认为Holger&Sotirios'答案是准确的,但因为我是发表声明的人,我想我应该解释一下。

我主要谈论spliterator characteristics,特别是SIZED特征。这基本上是"静态"有关在流程设置时已知但在流实际执行之前已知的流阶段的信息。实际上,它用于确定流的执行策略,因此必须在流执行之前知道它。

limit()操作会创建一个包装其上游分裂器的分裂器,因此limit分裂器需要确定要返回的特征。即使其上游分裂器为SIZED,它也不知道确切的大小,因此必须关闭SIZED特征。

所以如果你是程序员,那就写:

IntStream.range(0, 100).limit(10)

你说当然该流正好有10个元素。 (它会。)但是得到的分裂器仍然不是SIZED。毕竟,limit运营商并不知道上述与此之间的区别:

IntStream.range(0, 1).limit(10)

至少在分裂者特征方面。

这就是为什么,即使有时它看起来应该如此,limit运算符也不会返回已知大小的流。这反过来又会影响分裂策略,从而影响并行效率。