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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23218874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 21:24:16  来源:igfitidea点击:

What is difference between Collection.stream().forEach() and Collection.forEach()?

javacollectionsjava-8java-stream

提问by VladS

I understand that with .stream(), I can use chain operations like .filter()or use parallel stream. But what is difference between them if I need to execute small operations (for example, printing the elements of the list)?

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

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

采纳答案by Stuart Marks

For simple cases such as the one illustrated, they are mostly the same. However, there are a number of subtle differences that might be significant.

对于简单的情况,例如所示的情况,它们大体相同。但是,有许多细微的差异可能很重要。

One issue is with ordering. With Stream.forEach, the order is undefined. It's unlikely to occur with sequential streams, still, it's within the specification for Stream.forEachto execute in some arbitrary order. This does occur frequently in parallel streams. By contrast, Iterable.forEachis always executed in the iteration order of the Iterable, if one is specified.

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

Another issue is with side effects. The action specified in Stream.forEachis required to be non-interfering. (See the java.util.stream package doc.) Iterable.forEachpotentially has fewer restrictions. For the collections in java.util, Iterable.forEachwill generally use that collection's Iterator, most of which are designed to be fail-fastand which will throw ConcurrentModificationExceptionif the collection is structurally modified during the iteration. However, modifications that aren't structural areallowed during iteration. For example, the ArrayList class documentationsays "merely setting the value of an element is not a structural modification." Thus, the action for ArrayList.forEachis allowed to set values in the underlying ArrayListwithout problems.

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

The concurrent collections are yet again different. Instead of fail-fast, they are designed to be weakly consistent. The full definition is at that link. Briefly, though, consider ConcurrentLinkedDeque. The action passed to its forEachmethod isallowed to modify the underlying deque, even structurally, and ConcurrentModificationExceptionis never thrown. However, the modification that occurs might or might not be visible in this iteration. (Hence the "weak" consistency.)

并发集合再次不同。它们不是快速失败,而是设计为弱一致性。完整的定义在那个链接。不过,简而言之,请考虑ConcurrentLinkedDeque. 传递给它的forEach方法的动作允许修改底层双端队列,甚至是结构上的,并且ConcurrentModificationException永远不会被抛出。但是,发生的修改在此迭代中可能可见,也可能不可见。(因此“弱”一致性。)

Still another difference is visible if Iterable.forEachis iterating over a synchronized collection. On such a collection, Iterable.forEachtakes the collection's lockonce and holds it across all the calls to the action method. The Stream.forEachcall uses the collection's spliterator, which does not lock, and which relies on the prevailing rule of non-interference. The collection backing the stream could be modified during iteration, and if it is, a ConcurrentModificationExceptionor inconsistent behavior could result.

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

回答by skiwi

There is no difference between the two you have mentioned, atleast conceptually, the Collection.forEach()is just a shorthand.

您提到的两者之间没有区别,至少在概念上,这Collection.forEach()只是速记。

Internally the stream()version has somewhat more overhead due to object creation, but looking at the running time it neither has an overhead there.

stream()由于对象创建,该版本在内部有更多的开销,但从运行时间来看,它也没有开销。

Both implementations end up iterating over the collectioncontents once, and duringthe iteration print out the element.

两种实现最终都会对collection内容进行一次迭代,并迭代期间打印出元素。

回答by Angelo Fuchs

This answer concerns itself with the performance of the various implementations of the loops. Its only marginally relevant for loops that are called VERY OFTEN (like millions of calls). In most cases the content of the loop will be by far the most expensive element. For situations where you loop really often, this might still be of interest.

这个答案本身与循环的各种实现的性能有关。它仅与经常调用(如数百万次调用)的循环无关。在大多数情况下,循环的内容将是迄今为止最昂贵的元素。对于您经常循环的情况,这可能仍然很有趣。

You should repeat this tests under the target system as this is implementation specific, (full source code).

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

I run openjdk version 1.8.0_111 on a fast Linux machine.

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

I wrote a test that loops 10^6 times over a List using this code with varying sizes for integers(10^0 -> 10^5 entries).

我编写了一个测试,该测试使用此代码在 List 上循环 10^6 次,其中包含不同大小的integers(10^0 -> 10^5 个条目)。

The results are below, the fastest method varies depending on the amount of entries in the list.

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

But still under worst situations, looping over 10^5 entries 10^6 times took 100 seconds for the worst performer, so other considerations are more important in virtually all situations.

但仍然在最坏的情况下,循环超过 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;
    });
}

Here are my timings: milliseconds / function / number of entries in list. Each run is 10^6 loops.

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

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

If you repeat the experiment, I posted the full source code. Please do edit this answer and add you results with a notation of the tested system.

如果你重复这个实验,我发布了完整的源代码。请编辑此答案并添加带有测试系统符号的结果。



Using a MacBook Pro, 2.5 GHz Intel Core i7, 16 GB, macOS 10.12.6:

使用 MacBook Pro、2.5 GHz Intel Core i7、16 GB、macOS 10.12.6:

                           1    10    100    1000    10000
       iterator.forEach   27   106   1047    8516    88044
               for:each   46   143   1182   10548   101925
         for with index   49   145    887    7614    81130
iterable.stream.forEach  393   397   1108    8908    88361


Java 8 Hotspot VM - 3.4GHz Intel Xeon, 8 GB, Windows 10 Pro

Java 8 Hotspot VM - 3.4GHz Intel Xeon,8 GB,Windows 10 Pro

                            1    10    100    1000    10000
        iterator.forEach   30   115    928    8384    85911
                for:each   40   125   1166   10804   108006
          for with index   30   120    956    8247    81116
 iterable.stream.forEach  260   237   1020    8401    84883


Java 11 Hotspot VM - 3.4GHz Intel Xeon, 8 GB, Windows 10 Pro
(same machine as above, different JDK version)

Java 11 Hotspot VM - 3.4GHz Intel Xeon,8 GB,Windows 10 Pro
(与上述相同的机器,不同的 JDK 版本)

                            1    10    100    1000    10000
        iterator.forEach   20   104    940    8350    88918
                for:each   50   140    991    8497    89873
          for with index   37   140    945    8646    90402
 iterable.stream.forEach  200   270   1054    8558    87449


Java 11 OpenJ9VM - 3.4GHz Intel Xeon, 8 GB, Windows 10 Pro
(same machine and JDK version as above, different VM)

Java 11 OpenJ9VM - 3.4GHz Intel Xeon,8 GB,Windows 10 Pro
(与上述相同的机器和 JDK 版本,不同的 VM)

                            1    10    100    1000    10000
        iterator.forEach  211   475   3499   33631   336108
                for:each  200   375   2793   27249   272590
          for with index  384   467   2718   26036   261408
 iterable.stream.forEach  515   714   3096   26320   262786


Java 8 Hotspot VM - 2.8GHz AMD, 64 GB, Windows Server 2016

Java 8 Hotspot VM - 2.8GHz AMD,64 GB,Windows Server 2016

                            1    10    100    1000    10000
        iterator.forEach   95   192   2076   19269   198519
                for:each  157   224   2492   25466   248494
          for with index  140   368   2084   22294   207092
 iterable.stream.forEach  946   687   2206   21697   238457


Java 11 Hotspot VM - 2.8GHz AMD, 64 GB, Windows Server 2016
(same machine as above, different JDK version)

Java 11 Hotspot VM - 2.8GHz AMD,64 GB,Windows Server 2016
(与上述相同的机器,不同的 JDK 版本)

                            1    10    100    1000    10000
        iterator.forEach   72   269   1972   23157   229445
                for:each  192   376   2114   24389   233544
          for with index  165   424   2123   20853   220356
 iterable.stream.forEach  921   660   2194   23840   204817


Java 11 OpenJ9VM - 2.8GHz AMD, 64 GB, Windows Server 2016
(same machine and JDK version as above, different VM)

Java 11 OpenJ9VM - 2.8GHz AMD,64 GB,Windows Server 2016
(与上述相同的机器和 JDK 版本,不同的 VM)

                            1    10    100    1000    10000
        iterator.forEach  592   914   7232   59062   529497
                for:each  477  1576  14706  129724  1190001
          for with index  893   838   7265   74045   842927
 iterable.stream.forEach 1359  1782  11869  104427   958584


The VM implementation you choose also makes a difference Hotspot/OpenJ9/etc.

您选择的 VM 实现也会对 Hotspot/OpenJ9/等产生影响。

回答by cpatel

Collection.forEach() uses the collection's iterator (if one is specified). That means that the processing order of the items is defined. In contrast, the processing order of Collection.stream().forEach() is undefined.

Collection.forEach() 使用集合的迭代器(如果指定了一个)。这意味着定义了项目的处理顺序。相比之下,Collection.stream().forEach() 的处理顺序是未定义的。

In most cases, it doesn't make a difference which of the two we choose. Parallel streams allow us to execute the stream in multiple threads, and in such situations, the execution order is undefined. Java only requires all threads to finish before any terminal operation, such as Collectors.toList(), is called. Let's look at an example where we first call forEach() directly on the collection, and second, on a parallel stream:

在大多数情况下,我们选择两者中的哪一个并没有什么区别。并行流允许我们在多个线程中执行流,在这种情况下,执行顺序是不确定的。Java 只要求所有线程在调用任何终端操作(例如 Collectors.toList())之前完成。让我们看一个例子,我们首先直接在集合上调用 forEach(),然后在并行流上调用:

list.forEach(System.out::print);
System.out.print(" ");
list.parallelStream().forEach(System.out::print);

If we run the code several times, we see that list.forEach() processes the items in insertion order, while list.parallelStream().forEach() produces a different result at each run. One possible output is:

如果我们多次运行代码,我们会看到 list.forEach() 按插入顺序处理项目,而 list.parallelStream().forEach() 每次运行都会产生不同的结果。一种可能的输出是:

ABCD CDBA

Another one is:

另一种是:

ABCD DBCA