Collection.stream()。forEach()和Collection.forEach()之间有什么区别?

我明白,使用.stream() ,我可以使用像.filter()或使用并行stream链操作。 但是,如果我需要执行小操作(例如,打印列表元素),它们之间有什么区别呢?

 collection.stream().forEach(System.out::println); collection.forEach(System.out::println); 

对于简单的例子,如图所示,它们大部分是相同的。 但是,有一些细微的差异可能是重要的。

一个问题是订购。 使用Stream.forEach ,订单是未定义的 。 在顺序stream中不太可能发生,但它仍然在Stream.forEach的规范中以某种任意顺序执行。 这在并行stream中经常发生。 相比之下, Iterable.forEach总是以Iterable.forEach的迭代顺序执行,如果指定的话。

另一个问题是副作用。 Stream.forEach指定的操作必须是无干扰的 。 (请参阅java.util.stream包doc 。) Iterable.forEach可能具有更less的限制。 对于java.util的集合, Iterable.forEach通常会使用该集合的Iterator ,其中大部分被devise为快速失败,并且如果集合在迭代期间在结构上被修改,将抛出ConcurrentModificationException 。 但是,在迭代期间允许不是结构的修改。 例如, ArrayList类文档说“仅仅设置一个元素的值不是一个结构修改”。 因此,允许ArrayList.forEach的操作可以在底层ArrayList设置值,而不会出现任何问题。

并发集合又是不同的。 它们不是快速失败,而是被devise成弱一致的 。 完整的定义就在这个环节。 简而言之,考虑一下ConcurrentLinkedDeque 。 传递给它的forEach方法的动作允许修改底层的双层结构,甚至在结构上也是如此,并且永远不会抛出ConcurrentModificationExceptionexception。 但是,在此迭代中发生的修改可能会或可能不会显示。 (因此,“弱”的一致性。)

如果Iterable.forEach迭代同步的集合,还有另一个区别。 在这样一个集合上, Iterable.forEach一次获取集合的锁 ,并在所有对动作方法的调用中保持它。 Stream.forEach调用使用集合的分割器,该分割器不locking,并依赖于普遍的不干涉规则。 支持stream的集合可以在迭代期间修改,如果是,则可能导致ConcurrentModificationException或不一致的行为。

你提到的两个没有区别,至less在概念上, Collection.forEach()只是一个简写。

在内部,由于创build对象, stream()版本有更多的开销,但是查看运行时间,它在那里没有开销。

这两个实现最终迭代collection内容一次,并迭代期间打印出元素。

这个答案涉及到循环的各种实现的性能。 它与被称为非常(甚至是数百万次调用)的循环只有很小的相关性。 在大多数情况下,循环的内容将是迄今为止最昂贵的元素。 对于经常循环的情况,这可能仍然是有趣的。

您应该在目标系统下重复这个testing,因为这是特定于实现的( 完整源代码 )。

我在一台快速的Linux机器上运行openjdk 1.8.0_111版本。

我写了一个testing,使用这个代码在integers (10 ^ 0 – > 10 ^ 5个条目)上有不同的大小,在一个List上循环10 ^ 6次。

结果如下,最快的方法取决于列表中的条目数量。

但是在最糟糕的情况下,10 ^ 5次循环10 ^ 6次最差的表演者需要100秒,所以其他的考虑在几乎所有情况下都更为重要。

 public int outside = 0; private void forCounter(List<Integer> integers) { for(int ii = 0; ii < integers.size(); ii++) { Integer next = integers.get(ii); outside = next*next; } } private void forEach(List<Integer> integers) { for(Integer next : integers) { outside = next * next; } } private void iteratorForEach(List<Integer> integers) { integers.forEach((ii) -> { outside = ii*ii; }); } private void iteratorStream(List<Integer> integers) { integers.stream().forEach((ii) -> { outside = ii*ii; }); } 

这是我的计时:毫秒/function/列表中的条目数。 每个运行是10 ^ 6循环。

  1 10 100 1000 10000 for with index 39 112 920 8577 89212 iterator.forEach 27 116 959 8832 88958 for:each 53 171 1262 11164 111005 iterable.stream.forEach 255 324 1030 8519 88419 

如果您重复实验,我发布了完整的源代码 。 请做编辑这个答案,并添加结果与testing系统的记号。