简体   繁体   English

我应该在Java 8 Streams中使用共享的可变变量更新吗?

[英]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: 从功能上讲,对于简单的情况它们几乎是相同的,但一般来说,存在一些隐藏的差异:

  1. Lets start by quoting from Javadoc of 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-


  1. The order of Stream.forEach is random while Iterable.forEach is always executed in the iteration order of the Iterable . 顺序Stream.forEach虽然是随机的Iterable.forEach在的迭代顺序始终执行Iterable

  1. If 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,它不会锁定

  1. The action specified in 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设置值而不会出现问题。

  1. In Java, Iterators returned by Collection classes, eg ArrayList, HashSet, Vector, etc., are fail fast. 在Java中,Collection类返回的迭代器(例如ArrayList,HashSet,Vector等)快速失败。 This means that if you try to add() or remove() from the underlying data structure while iterating it, you get a 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM