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
What is difference between Collection.stream().forEach() and Collection.forEach()?
提问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.forEach
to execute in some arbitrary order. This does occur frequently in parallel streams. By contrast, Iterable.forEach
is always executed in the iteration order of the Iterable
, if one is specified.
一个问题是订购。使用Stream.forEach
,顺序未定义。它不太可能发生在顺序流中,但它仍然符合Stream.forEach
以某种任意顺序执行的规范。这在并行流中经常发生。相比之下,如果指定Iterable.forEach
了Iterable
,则始终按照 的迭代顺序执行。
Another issue is with side effects. The action specified in Stream.forEach
is required to be non-interfering. (See the java.util.stream package doc.) Iterable.forEach
potentially has fewer restrictions. For the collections in java.util
, Iterable.forEach
will generally use that collection's Iterator
, most of which are designed to be fail-fastand which will throw ConcurrentModificationException
if 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.forEach
is allowed to set values in the underlying ArrayList
without problems.
另一个问题是副作用。中指定的操作Stream.forEach
必须是非干扰性的。(请参阅java.util.stream 包 doc。)Iterable.forEach
可能具有较少的限制。对于 中的集合java.util
,Iterable.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 forEach
method isallowed to modify the underlying deque, even structurally, and ConcurrentModificationException
is 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.forEach
is iterating over a synchronized collection. On such a collection, Iterable.forEach
takes the collection's lockonce and holds it across all the calls to the action method. The Stream.forEach
call 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 ConcurrentModificationException
or 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 collection
contents 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