成为Java流大师–第3部分:终端操作
比爾·蓋茨曾經說過:“我選擇一個懶惰的人去做一件困難的事情,因為一個懶惰的人會找到一個簡單的方法來做。” 關于流,沒有什么比這更真實了。 在本文中,您將學習Stream如何通過在調用終端操作之前不對源元素執行任何計算來避免不必要的工作,以及源如何只生成最少數量的元素。
 本文是五分之三,以GitHub存儲庫為補充,其中包含每個單元的說明和練習。 
 第1部分:創建流 
 第2部分:中級操作 第三部分:終端操作 第4部分:數據庫流 第5部分:使用流創建數據庫應用程序 
終端機操作
 現在我們熟悉Stream管道的初始化和構造,我們需要一種處理輸出的方法。 終端操作通過從其余元素(例如 
 count() )或副作用(例如 
 forEach(Consumer) )。 
在啟動終端操作之前,Stream將不會對源的元素執行任何計算。 這意味著僅在需要時才使用源元素,這是避免不必要工作的明智方法。 這也意味著一旦應用了終端操作,流將被消耗,并且無法再添加其他操作。
讓我們看一下可以應用于Stream管道末尾的哪些終端操作:
ForEach和ForEachOrdered
流的可能用例可能是更新某些或所有元素的屬性,或者為什么不只是出于調試目的而將它們打印出來。 無論哪種方式,我們都不希望收集或計數輸出,而是通過產生副作用而不返回值來進行。
 這是目的 
 forEach()或 
 forEachOrdered() 。 他們倆都 Consumer并終止Stream,而不返回任何內容。 這些操作之間的區別僅僅是 forEachOrdered()承諾按照元素在Stream中出現的順序調用提供的Consumer。 forEach()僅承諾以任何順序調用Consumer。 后一種變體對并行流很有用。 
在下面的簡單情況下,我們在一行中打印出Stream的每個元素。
Stream.of( "Monkey" , "Lion" , "Giraffe" , "Lemur" , “Lion” ) .forEachOrdered(System.out::print);這將產生以下輸出:
MonkeyLionGiraffeLemurLion <br>收集元素
 Streams的常見用法是構建元素的“存儲桶”,或更具體地說,構建包含特定元素集合的數據結構。 這可以通過調用終端操作來完成 
 Stream末尾的collect() ,因此要求它將元素收集到給定的數據結構中。 我們可以提供稱為 
 Collector collect()操作,可以根據手頭的問題使用許多不同的預定義類型。 以下是一些非常有用的選項: 
收集到設置
 我們可以將所有元素收集到 
 通過使用收集器收集Stream的元素來簡單地進行Set 
 toSet() 。 
收集到清單
 同樣,可以將元素收集到 
 List使用 
 toList()收集器。 
收集到一般收藏
 在更一般的情況下,可以將Stream的元素收集到任何 
 通過僅提供所需構造函數的Collection 
 Collection類型。 構造函數的例子是 LinkedList::new , LinkedHashSet::new和 PriorityQueue::new 
 收集到陣列 
 由于數組是固定大小的容器,而不是靈活的容器 Collection ,有充分的理由進行特殊的終端操作, toArray() ,以將元素創建并存儲在Array中。 請注意,僅調用toArray()會導致Objects Array ,因為該方法無法自行創建類型化數組。 下面我們展示如何使用String數組的構造函數來提供類型化的數組String[] 。 
收集到地圖
 我們可能想從元素中提取信息,并將結果提供為Map 。 為此,我們使用收集器toMap() ,它需要兩個 
 Functions按鍵對應的映射器和值映射器。 
該示例顯示了不同的動物如何與它們名稱中不同字符的數量相關聯。 我們使用中間操作distinct()來確保僅在Map添加唯一鍵(如果鍵不是唯一的,則必須提供toMap()收集器的變體,其中必須提供用于合并的解析器來自相等鍵的結果)。
Map<String, Integer> toMap = Stream.of( "Monkey" , "Lion" , "Giraffe" , "Lemur" , "Lion" ) .distinct() .collect(Collectors.toMap( Function.identity(), //Function<String, K> keyMapper s -> ( int ) s.chars().distinct().count() // Function<String, V> valueMapper )); toMap: {Monkey= 6 , Lion= 4 , Lemur= 5 , Giraffe= 6 } (*)(*)請注意,鍵順序是未定義的。
收集分組
 堅持使用桶的類比,我們實際上可以同時處理多個桶。 有一個非常有用的Collector名為 
 groupingBy()根據某些屬性將元素劃分為不同的組,從而通過稱為“分類器”的某種內容提取該屬性。 這樣的操作的輸出是Map 。 下面我們演示如何根據動物的名字的首字母對動物進行分組。 
使用下游收集器收集分組
在前面的示例中,默認情況下,將“下游收集器” toList()應用于Map的值,將每個存儲桶的元素收集到List 。 有一個重載版本的groupingBy() ,它允許使用自定義的“下游收集器”來更好地控制生成的Map 。 下面是一個示例,說明如何將特殊的下游收集器counting()用于計數(而不是收集)每個存儲區的元素。
Map<Character, Long> groupingByCounting = Stream.of( "Monkey" , "Lion" , "Giraffe" , "Lemur" , "Lion" ) .collect(Collectors.groupingBy( s -> s.charAt( 0 ), // Function<String, K> classifier counting() // Downstream collector )); groupingByCounting: {G= 1 , L= 3 , M= 1 }這是該過程的說明:
任何收集器都可以用作下游收集器。 特別是,值得注意的是,收集器groupingBy()可以采用下游收集器,該下游收集器也是groupingBy()收集器,從而允許對第一分組操作的結果進行二次分組。 在我們的動物案例中,我們也許可以創建一個Map<Character, Map<Character, Long>> ,其中第一個地圖包含具有第一個字符的鍵,第二個地圖包含第二個字符作為鍵,出現次數作為值。
元素的出現
中間操作filter()是消除與給定謂詞不匹配的元素的好方法。 盡管在某些情況下,我們只是想知道是否存在至少一個滿足該謂詞的元素。 如果是這樣,使用anyMatch()會更方便和有效。 在這里,我們尋找數字2的出現:
boolean containsTwo = IntStream.of( 1 , 2 , 3 ).anyMatch(i -> i == 2 ); containsTwo: true計算操作
 幾個終端操作輸出計算結果。 我們可以執行的最簡單的計算是count() ,它可以應用于任何 
 Stream. 例如,它可以用于計算動物數量: 
 雖然,某些終端操作僅適用于我們在第一篇文章中提到的特殊Stream實現。 IntStream , 
 LongStream和DoubleStream 。 可以訪問此類流,我們可以簡單地將所有元素匯總如下: 
或者為什么不使用.average()計算整數的平均值:
OptionalDouble average = IntStream.of( 1 , 2 , 3 ).average(); average: OptionalDouble[ 2.0 ]或使用.max()檢索最大值。
int max = IntStream.of( 1 , 2 , 3 ).max().orElse( 0 ); max: 3像average()一樣, max()運算符的結果是Optional ,因此通過聲明.orElse(0)我們自動檢索該值(如果存在或默認為0)。 如果我們寧愿處理原始返回類型,也可以將相同的解決方案應用于平均示例。
 如果我們對所有這些統計數據都感興趣,那么創建幾個相同的流并對每個流應用不同的終端操作是非常麻煩的。 幸運的是,有一個方便的操作稱為summaryStatistics() ,它允許將幾個常見的統計屬性合并到一個 
 SummaryStatistics對象。 
練習題
希望您現在熟悉所提供練習的格式。 如果您只是發現了該系列或者最近才感到有點懶惰(也許您也有自己的理由),我們建議您克隆GitHub存儲庫并開始使用后續材料。 本文的內容足以解決名為MyUnit3Terminal的第三個單元。 相應的Unit3Terminal接口包含JavaDocs,它們描述MyUnit3Terminal方法的預期實現。
public interface Unit3Terminal { /** * Adds each element in the provided Stream * to the provided Set. * * An input stream of ["A", "B", "C"] and an * empty input Set will modify the input Set * to contain : ["A", "B", "C"] * * @param stream with input elements * @param set to add elements to */ void addToSet(Stream stream, Set set); <br>提供的測試(例如Unit3MyTerminalTest)將充當自動評分工具,讓您知道您的解決方案是否正確。
下一篇
 下一篇文章將展示如何將到目前為止我們積累的所有知識應用于數據庫查詢。 
 提示:再見SQL,Hello Streams…直到那時–編碼愉快! 
s
 Per Minborg 
 Julia·古斯塔夫森(Julia Gustafsson) 
翻譯自: https://www.javacodegeeks.com/2019/10/become-master-java-streams-terminal-operations.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的成为Java流大师–第3部分:终端操作的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 电脑老是清理内存好吗(为什么电脑清理之后
- 下一篇: c 遍历文件 递归遍历_将递归文件系统遍
