Java 8 | Stream (上)
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理資料。Stream使用一種類似用SQL語句從資料庫查詢資料的直觀方式來提供一種對Java集合運算和表達的高階抽象。Stream API可以極大提高Java工程師的生產力,讓工程師寫出高效率、乾淨、簡潔的程式碼。
這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。
什麼是 Stream?
Stream(流)是一個來自資料源的元素隊列並支持聚合操作
- 元素是特定類型的對象,形成一個隊列。
Java中的Stream並不會存儲元素,而是按需計算。 - 資料源 流的來源。可以是集合,數組,
I/O channel, 產生器generator等。 - 聚合操作 類似SQL語句一樣的操作, 比如
filter,map,reduce,find,match,sorted等。
和以前的Collection操作不同, Stream操作還有兩個基礎的特徵:
Pipelining:中間操作都會返回流對象本身。這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路(short-circuiting)。- 內部迭代: 以前對集合遍歷都是通過
Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。
生成流
在Java 8中, 集合接口有兩個方法來生成流:
- stream() − 為集合創建串行流。
- parallelStream() − 為集合創建並行流。
1 | List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
forEach
Stream提供了新的方法forEach來迭代流中的每個資料。以下程式碼片段使用forEach輸出了10個隨機數:
1 | Random random = new Random(); |
輸出的結果為:(每次都不一樣)
1 | -66046892 |
map
map方法用於映射每個元素到對應的結果,以下程式碼片段使用map輸出了元素對應的平方數:
1 | List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); |
squaresList為:
1 | [9, 4, 49, 25] |
filter
filter方法用於通過設置的條件過濾出元素。以下程式碼片段使用 filter方法過濾出空字符串:
1 | List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
count為:
1 | 2 |
limit
limit方法用於獲取指定數量的流。以下程式碼片段使用limit方法印出10條資料:
1 | Random random = new Random(); |
輸出的結果為:(每次都不一樣)
1 | 817080995 |
sorted
sorted方法用於對流進行排序。以下程式碼片段使用sorted方法對輸出的10個隨機數進行排序:
1 | Random random = new Random(); |
輸出的結果為:(排序)
1 | -1374877061 |
統計
另外,一些產生統計結果的收集器也非常有用。它們主要用於int、double、long等基本類型上,它們可以用來產生類似如下的統計結果。
1 | List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); |
輸出的結果為:
1 | 列表中最大的數 : 7 |
簡單流程說明
1 | +--------------------+ +------+ +------+ +---+ +-------+ |
以上的流程轉換為Java程式為:
1 | List<Integer> transactionsIds = |
Stream之上的操作可分為中間操作和晚期操作
中間操作:會返回一個新的stream——執行一個中間操作(例如filter)並不會執行實際的過濾操作,而是創建一個新的stream,並將原stream中符合條件的元素放入新創建的stream。
晚期操作:(例如forEach或者sum),會遍歷stream並得出結果或者附帶結果;在執行晚期操作之後,stream處理線已經處理完畢,就不能使用了。在幾乎所有情況下,晚期操作都是立刻對stream進行遍歷。
範例
Stream API極大得簡化了集合操作(不止是集合),首先看下這個叫Task的類:
1 | public class Streams { |
Task類有一個分數(或偽複雜度)的概念,另外還有兩種狀態:OPEN或者CLOSED。現在假設有一個task集合:
1 | final Collection< Task > tasks = Arrays.asList( |
首先:在這個task集合中一共有多少個OPEN狀態的點?在Java 8之前,要解決這個問題,則需要使用foreach循環遍歷task集合;但是在Java 8中可以利用streams解決:包括一系列元素的列表,並且支持順序和並行處理。
1 | // Calculate total points of all active tasks using sum() |
輸出的結果為:
1 | Total points: 18 |
tasks集合被轉換成stream表示;stream上的filter操作會過濾掉所有CLOSED的task;mapToInt操作基於每個task實例的Task::getPoints方法將task流轉換成Integer集合;- 通過
sum方法計算總和,得出最後的結果。
總結
這邊大致上稍為介紹了Stream的使用,透過範例並且實際去執行,在將來使用的時候才會有所印象;而這邊將Stream分成上下篇,下篇將會針對parallel和Collectors特別說明。
註:以上參考了
Stream 與平行化
Java 8 Lambda新語法,簡化程式,增強效能
Java 8 新特性
Java 8的新特性—终极版
现代化 Java - Java8 指南
