Stream#limit return 元素可以比预期少吗?

Can Stream#limit return fewer elements than expected?

如果下面的 Stream s 至少有 n 个元素,那么在什么情况下 Stream sLimit 可能有少于 n 个元素(如果有)?

Stream sLimit = s.limit(n);

提问原因:在,我看到:

Despite the appearances, using limit(10) doesn't necessarily result in a SIZED stream with exactly 10 elements -- it might have fewer.

你误解了这个说法。如果 Stream 至少有 n 个元素并且你在其上调用 limit(n),它将恰好有 n 个元素但是 Stream 实现 可能没有意识到这一点,因此性能不尽如人意。

相比之下,某些 Stream 来源 (Spliterators) 肯定知道它们具有固定大小,例如创建 Stream for an array or an IntStream via IntStream.range 时。它们可以比 Streamlimit(n).

更好地优化

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

我认为 Holger 和 Sotirios 的回答是准确的,但鉴于我是发表声明的人,我想我应该解释一下自己。

我主要说的是spliterator characteristics,特别是SIZED这个特性。这基本上是关于流阶段的 "static" 信息,这些信息在管道设置时已知,但在流实际执行之前。实际上,它用于确定流的执行策略,因此必须在流执行之前知道它。

limit() 操作创建了一个拆分器来包装其上游拆分器,因此 limit 拆分器需要确定要 return 的特征。即使它的上游分裂器是SIZED,它也不知道确切的大小,所以它不得不关闭SIZED特性。

所以如果你,程序员,要写:

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

你会说 当然 流恰好有 10 个元素。 (它会的。)但是生成的拆分器仍然不是 SIZED。毕竟limit算子不知道上面和这个的区别:

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

至少在分离器特性方面。

这就是为什么,尽管有时看起来应该如此,但 limit 运算符并没有 return 已知大小的流。这反过来会影响拆分策略,进而影响并行效率。