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

时间:2018-09-21 07:52:39

标签: java java-8 java-stream

只需在列表下面进行迭代并通过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);

以上三个迭代之间有什么区别,我们需要使用哪一个。有什么考虑吗?

3 个答案:

答案 0 :(得分:4)

无论您使用并行Stream还是顺序forEach,当目标是生成List时都不应使用map。将collectList<String> list2 = list2.stream() .map(item -> item.startsWith("A") ? item : "EMPTY") .collect(Collectors.toList()); 一起使用:

var_dump()

答案 1 :(得分:3)

从功能上讲,对于简单案例,它们几乎是相同的,但总的来说,存在一些隐藏的差异:

  1. 让我们从引用forEach的Javadoc开始,列举可重复的用例,说明:
  

对Iterable的每个元素执行给定的动作,直到所有   元素已处理或操作引发异常。

我们还可以遍历一个集合并在每个元素上执行给定的操作-只需传递一个实现Consumer接口的类

void forEach(Consumer<? super T> action)

https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-


  1. Stream.forEach的顺序是随机的,而Iterable.forEach总是按照Iterable的迭代顺序执行。

  1. 如果Iterable.forEach正在遍历同步的集合,则Iterable.forEach会获取一次该集合的锁,并将其保存在对action方法的所有调用中。 Stream.forEach调用使用集合的分隔符,该分隔符不会锁定

  1. Stream.forEach中指定的操作必须是不干扰,而允许Iterable.forEach在基础ArrayList中设置值没有问题。

  1. 在Java中,Collection类返回的迭代器,例如ArrayList,HashSet,Vector等快速失败。这意味着,如果您在迭代过程中尝试从基础数据结构中进行add()或remove(),则会得到ConcurrentModificationException.

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#fail-fast


更多信息:

  

答案 2 :(得分:0)

我个人认为,在使用流时,应该以一种方式编写代码,如果切换到并行流,则不会破坏代码(产生错误的结果)。 想象一下,如果在代码中您正在同一共享内存(列表2)上进行读写,并且将进程分布到多个线程中(使用并行流)。那你就是DOOMED。因此,您有几种选择。

使共享内存(list2)线程安全。例如使用AtomicReferences

List<String> list2 = new ArrayList<>();
AtomicReference<List<String>> listSafe = new AtomicReference<>();
listSafe.getAndUpdate(strings -> {strings.add("newvalue"); return strings;}); 

或者您可以使用纯功能方法(无副作用的代码) 就像@Eran解决方案一样。