天天看點

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

對集合操作時,因不同的寫法Idea經常會提示:The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

翻譯:'stream()。forEach()'鍊可以替換為'forEach()'(可能會改變語義)

解釋:對集合操作推薦直接使用Collection.forEach(),如果不需要流的話

流:流的好處不必多說

現象:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

 擴充說明:

1.簡介

在java中有多種方式對集合進行周遊。本教程中将看兩個類似的方法 Collection.stream().forEach()和Collection.forEach()。

在大多數情況下,兩者都會産生相同的結果,但是,我們會看到一些微妙的差異。

2.概述

首先,建立一個疊代清單:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

最直接的方法是使用增強的for循環:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

如果我們想使用函數式Java,我們也可以使用forEach()。我們可以直接在集合上這樣做:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

或者,我們可以在集合的流上調用forEach():

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

兩個版本都将疊代清單并列印所有元素:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

在這個簡單的例子中,我們使用的forEach()沒有差別。

3.執行順序

Collection.forEach()使用集合的疊代器(如果指定了一個),集合裡元素的處理順序是明确的。相反,Collection.stream().forEach()的處理順序是不明确的。

在大多數情況下,我們選擇上述兩種方式的哪一種是沒有差別的。但是有時候有。

3.1 Parallel Stream

并發流允許我們在多個線程中執行stream,在這種情況下,執行順序也不明确的。Java隻需要在調用任何最終操作(例如Collectors.toList())之前完成所有線程。

看一個例子,首先直接在集合上調用forEach(),然後在并發流上調用:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

如果我們多次運作代碼,我們會看到list.forEach()以插入順序處理元素,而 list.parallelStream().forEach()在每次運作會産生不同的結果。

一個可能的輸出是:

ABCD CDBA

另一個是:

ABCD BDCA

3.2 自定義疊代器

讓我們使用自定義疊代器定義一個清單,以反向順序疊代集合:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

當我們周遊清單時,再次使用forEach()直接在集合上,然後在流上:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

我們得到不同的結果:

DCBA  ABCD

結果不同的原因是在清單中使用的forEach()會使用自定義疊代器,而stream().forEach()隻是從清單中逐個擷取元素,會忽略疊代器。

4.修改集合

很多集合在周遊的時候,不應該在結構上被修改(比如ArrayList或HashSet)。如果在疊代期間删除或添加元素,會抛出ConcurrentModification異常。

此外,集合設計為快速失敗(fail-fast),這意味着一旦修改就抛出異常。

類似地,當我們在stream的執行期間添加或删除元素時,我們将得到ConcurrentModification異常。但是,異常将在稍後抛出。

兩個forEach()方法之間的另一個細微差别是Java明确允許使用疊代器修改元素。相反,stream不能。

來看一下更詳細的例子。

4.1 删除元素

定義一個清單,删除最後一個元素(“D”):

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

周遊清單時,在列印第一個元素(“A”)後删除最後一個元素:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

因為forEach()是快速失敗的,是以我們停止疊代并在處理下一個元素之前看到異常:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

讓我們看看如果我們使用stream().forEach()會發生什麼:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

在這裡,我們繼續疊代整個清單,然後才看到異常:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

但是,Java并不保證會抛出ConcurrentModificationException。這意味着我們永遠不應該編寫依賴于此異常的程式。

4.2 改變元素

我們可以在疊代清單時更改元素:

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

但是,雖然使用Collection.forEach()或stream()。forEach()執行此操作沒有問題,但Java要求對流的操作是無幹擾的。這意味着在執行流管道期間不應修改元素。

這背後的原因是流應該促進并行執行。在這裡,修改流的元素可能會導緻意外行為。

5.結論

在本文中,我們看到了一些示例,它們顯示了Collection.forEach()和Collection.stream().forEach()之間的細微差别。

但是,重要的是要注意上面顯示的所有示例僅僅是為了比較疊代集合的兩種方式。

如果我們不需要流但隻想疊代集合,則第一個選擇應該直接在集合上使用forEach()。

這也是idea建議我們直接使用foeeach的原因 !

The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)

參考:https://baijiahao.baidu.com/s?id=1637952388544934539&wfr=spider&for=pc