java - Collection.stream().forEach() 和 Collection.forEach() 有什么区别?

标签 java collections java-8 java-stream

据我了解,通过 .stream(),我可以使用 .filter() 等链式操作或使用并行流。但是如果我需要执行小操作(例如,打印列表的元素),它们之间有什么区别?

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

最佳答案

对于如图所示的简单情况,它们大多是相同的。但是,存在一些可能很重要的细微差别。

其中一个问题是订购。对于Stream.forEach,顺序是未定义。对于顺序流来说,这种情况不太可能发生,但它仍然符合 Stream.forEach 以某种任意顺序执行的规范。这在并行流中经常发生。相比之下,如果指定的话,Iterable.forEach 始终按照 Iterable 的迭代顺序执行。

另一个问题是副作用。 Stream.forEach 中指定的操作必须是非干扰。 (请参阅 java.util.stream package doc 。)Iterable.forEach 可能具有较少的限制。对于java.util中的集合,Iterable.forEach通常会使用该集合的Iterator,其中大部分被设计为 fail-fast如果集合在迭代期间发生结构性修改,则会抛出 ConcurrentModificationException。但是,在迭代过程中允许进行非结构性的修改。例如,ArrayList class documentation说“仅仅设置元素的值不是结构修改。”因此,ArrayList.forEach 的操作可以毫无问题地在底层 ArrayList 中设置值。

并发集合又有所不同。它们不是快速失败,而是被设计为 weakly consistent 。完整的定义位于该链接。不过,简单地考虑一下ConcurrentLinkedDeque。传递给其 forEach 方法的操作允许修改底层双端队列,甚至是结构上的修改,并且永远不会抛出 ConcurrentModificationException 。但是,发生的修改在此迭代中可能可见,也可能不可见。 (因此是“弱”一致性。)

如果 Iterable.forEach 迭代同步集合,则还可以看到另一个差异。在这样的集合上,Iterable.forEach takes the collection's lock一次并在对操作方法的所有调用中保留它。 Stream.forEach 调用使用集合的 spliterator,它不锁定,并且依赖于不干扰的普遍规则。支持流的集合可能会在迭代期间被修改,如果是这样,可能会导致 ConcurrentModificationException 或不一致的行为。

关于java - Collection.stream().forEach() 和 Collection.forEach() 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56687344/

相关文章:

java - 任何 HashMap 方法都不会调用覆盖的 hashCode() 函数

java - 如何在Android中可靠地拍照?

java - 使用可变键映射并发

java - 如何在 java 8 中使用流过滤两个列表对象并将值设置为新列表

generics - 显式调用 java 中的默认方法 - 当实现的接口(interface)使用泛型时

java - 集合的一对多查询

java - bufferedwriter 拒绝写入文本文件(Java)

python - isinstance(x, list) 在迭代包含字符串和列表的列表时

java - 如何使用流从 HashMap<E,R> 中提取 List<D>

java - 如何对集合子集应用进一步的过滤器