簡體   English   中英

我應該在Java 8 Streams中使用共享的可變變量更新嗎?

[英]Should I use shared mutable variable update in 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);

上面三次迭代和我們需要使用哪一次有什么區別。 有什么考慮嗎?

無論您是使用並行還是順序Stream ,當您的目標是生成List時,都不應該使用forEach 使用map collect

List<String> list2 = 
    list2.stream()
         .map(item -> item.startsWith("A") ? item : "EMPTY")
         .collect(Collectors.toList());

從功能上講,對於簡單的情況它們幾乎是相同的,但一般來說,存在一些隱藏的差異:

  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調用使用集合的spliterator,它不會鎖定

  1. Stream.forEach指定的操作必須是非干擾的,Iterable.forEach允許在基礎ArrayList設置值而不會出現問題。

  1. 在Java中,Collection類返回的迭代器(例如ArrayList,HashSet,Vector等)快速失敗。 這意味着如果在迭代它時嘗試從底層數據結構中添加()或remove(),則會出現ConcurrentModificationException.

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


更多信息:

我個人認為,在使用流時,您應該以一種方式編寫代碼,如果切換到並行流,它不會破壞您的代碼(產生錯誤的結果)。 想象一下,如果在你的代碼中你正在讀取和寫入相同的共享內存(list2),並將流程分配到多個線程(使用並行流)。 然后你就被詛咒了。 因此,您有幾種選擇。

使您的共享內存(list2)線程安全。 例如,通過使用AtomicReferences

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

或者您可以使用純粹的功能方法(沒有副作用的代碼),如@Eran解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM