[英]Should I use shared mutable variable update in Java 8 Streams
Just iterating below list & adding into another shared mutable list via java 8 streams. 只需在列表下方迭代并通过java 8流添加到另一个共享的可变列表中。
List<String> list1 = Arrays.asList("A1","A2","A3","A4","A5","A6","A7","A8","B1","B2","B3");
List<String> list2 = new ArrayList<>();
Consumer<String> c = t -> list2.add(t.startsWith("A") ? t : "EMPTY");
list1.stream().forEach(c);
list1.parallelStream().forEach(c);
list1.forEach(c);
What is the difference between above three iteration & which one we need to use. 上面三次迭代和我们需要使用哪一次有什么区别。 Are there any considerations? 有什么考虑吗?
Regardless of whether you use parallel or sequential Stream
, you shouldn't use forEach
when your goal is to generate a List
. 无论您是使用并行还是顺序Stream
,当您的目标是生成List
时,都不应该使用forEach
。 Use map
with collect
: 使用map
collect
:
List<String> list2 =
list2.stream()
.map(item -> item.startsWith("A") ? item : "EMPTY")
.collect(Collectors.toList());
Functionally speaking,for the simple cases they are almost the same, but generally speaking, there are some hidden differences: 从功能上讲,对于简单的情况它们几乎是相同的,但一般来说,存在一些隐藏的差异:
forEach
for iterable use-cases stating that: 让我们从forEach
Javadoc引用可迭代的用例开始,说明: performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. 对Iterable的每个元素执行给定的操作,直到处理完所有元素或者操作抛出异常为止。
and also we can iterate over a collection and perform a given action on each element – by just passing a class that implements the Consumer interface 我们还可以迭代一个集合并对每个元素执行一个给定的操作 - 只需传递一个实现Consumer接口的类
void forEach(Consumer<? super T> action)
https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer- https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-
Stream.forEach
is random while Iterable.forEach
is always executed in the iteration order of the Iterable
. 顺序Stream.forEach
虽然是随机的Iterable.forEach
在的迭代顺序始终执行Iterable
。 Iterable.forEach
is iterating over a synchronized collection, Iterable.forEach
takes the collection's lock once and holds it across all the calls to the action method. 如果Iterable.forEach
正在迭代同步集合,则Iterable.forEach
将获取集合的锁定并将其保存在对action方法的所有调用中。 The Stream.forEach call uses the collection's spliterator, which does not lock Stream.forEach调用使用集合的spliterator,它不会锁定 Stream.forEach
is required to be non-interfering while Iterable.forEach
is allowed to set values in the underlying ArrayList
without problems. Stream.forEach
指定的操作必须是非干扰的,而Iterable.forEach
允许在基础ArrayList
设置值而不会出现问题。 ConcurrentModificationException.
这意味着如果在迭代它时尝试从底层数据结构中添加()或remove(),则会出现ConcurrentModificationException.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#fail-fast https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#fail-fast
More Info: 更多信息:
I personally believe that when working with streams, you should write your code in a way that if you switch to parallel streams, it does not break your code (produce wrong results). 我个人认为,在使用流时,您应该以一种方式编写代码,如果切换到并行流,它不会破坏您的代码(产生错误的结果)。 Imagine if in your code you were doing reading and writing on the same shared memory (list2) and you distribute your process into several threads (using parallel streams). 想象一下,如果在你的代码中你正在读取和写入相同的共享内存(list2),并将流程分配到多个线程(使用并行流)。 Then you are DOOMED. 然后你就被诅咒了。 Therefore you have several options. 因此,您有几种选择。
make your shared memory (list2) thread safe. 使您的共享内存(list2)线程安全。 for example by using AtomicReferences 例如,通过使用AtomicReferences
List<String> list2 = new ArrayList<>();
AtomicReference<List<String>> listSafe = new AtomicReference<>();
listSafe.getAndUpdate(strings -> {strings.add("newvalue"); return strings;});
or you can go with the purely functional approach (code with no side effect) like the @Eran solution. 或者您可以使用纯粹的功能方法(没有副作用的代码),如@Eran解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.