Stream實現了對資料來源的流式處理,它可以並行操作,提高資料處理效率。
什麼是流
流不是集合,它不對資料做儲存,只是最資料進行演算法處理,比如最大值,最小值,排序等操作。Stream會在資料來源內部隱式的遍歷進行處理。Stream會並行遍歷資料,將資料分成若干段,同時進行處理,最終彙總結果一起輸出。
Stream 就如同一個迭代器(Iterator),單向,不可往復,資料只能遍歷一次,遍歷過一次後即用盡了,就好比流水從面前流過,一去不復返。
特點
首先對stream的操作可以分為兩類,中間操作(intermediate operations)和結束操作(terminal operations):
- 中間操作總是會惰式執行,呼叫中間操作只會生成一個標記了該操作的新stream。
- 結束操作會觸發實際計算,計算髮生時會把所有中間操作積攢的操作以pipeline的方式執行,這樣可以減少迭代次數。計算完成之後stream就會失效。
- 無儲存。stream不是一種資料結構,它只是某種資料來源的一個檢視,資料來源可以是一個陣列,Java容器或I/O channel等。
- 為函數語言程式設計而生。對stream的任何修改都不會修改背後的資料來源,比如對stream執行過濾操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream。
- 惰式執行。stream上的操作並不會立即執行,只有等到使用者真正需要結果的時候才會執行。
- 可消費性。stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。
使用方法
1.構造流的方法
public class StreamStudy {
public static void main(String[] args) throws Exception {
//1. of
Stream<String> stream = Stream.of("hello","java","python");
// 2. Arrays
String [] strArray = new String[] {"hello","java","python"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
System.out.println(stream.findAny());
}
}
最終只返回第一個結果: Optional[hello]
2. 流轉換為其它資料結構
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python"};
List<String> list = Arrays.asList(strArray);
// to array
System.out.println(list.stream().toArray()[0]);
// to list
System.out.println(list.stream().collect(Collectors.toList()));
// to string
System.out.println(list.stream().collect(Collectors.joining()).toString());
}
}
輸出:
hello
[hello, java, python]
hellojavapython
3. 流的操作
- Intermediate (map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered)
- Terminal(forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator)
- Short-circuiting(anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit)
一個流可以後面跟隨零個或多個 intermediate 操作。其目的主要是開啟流,做出某種程度的資料對映/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅呼叫到這類方法,並沒有真正開始流的遍歷。
一個流只能有一個 terminal 操作,當這個操作執行後,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。
4.基礎的使用
1.map
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python"};
List<String> list = Arrays.asList(strArray);
list.stream().map((v) ->v.toUpperCase())
.forEach(t -> System.out.println(t));
}
}
將list中的所有字母轉換成大寫,然後遍歷輸出。 實際list中的值並沒有改變,我們只是藉助Stream來做業務處理。
輸出 :
HELLO
JAVA
PYTHON
2.filter
+map
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python"};
List<String> list = Arrays.asList(strArray);
list.stream().filter(f -> f.length()>4)
.map(v ->v.toUpperCase())
.forEach(t -> System.out.println(t));
}
}
先filter過濾,然後map字母大寫,最後forEach輸出結果:
HELLO
PYTHON
3. filter
+sorted
+map
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python","node","react","vue"};
List<String> list = Arrays.asList(strArray);
list.stream().filter(f -> f.length()>3)
.sorted((a,b) -> b.compareTo(a))
.map(v ->v.toUpperCase())
.forEach(t -> System.out.println(t));
}
}
先filter過濾,然後sorted排序,然後map字母大寫,最後forEach輸出結果:
REACT
PYTHON
NODE
JAVA
HELLO
4. filter
+sorted
+map
+distinct
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
List<String> list = Arrays.asList(strArray);
list.stream().filter(f -> f.length()>3)
.sorted((a,b) -> b.compareTo(a))
.map(v ->v.toUpperCase())
.distinct()
.forEach(t -> System.out.println(t));
}
}
distinct去重,使用 Object.equals(Object)
來判斷是否重複,最終只留下一個REACT,結果:
REACT
PYTHON
NODE
JAVA
HELLO
5. filter
+sorted
+map
+distinct
+limit
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
List<String> list = Arrays.asList(strArray);
list.stream().filter(f -> f.length()>3)
.sorted((a,b) -> b.compareTo(a))
.map(v ->v.toUpperCase())
.distinct()
.limit(3)
.forEach(t -> System.out.println(t));
}
}
limit限制元素個數,這裡著要前3個結果:
REACT
PYTHON
NODE
6.filter
+sorted
+map
+distinct
+limit
+peek
+forEach
public class StreamStudy {
public static void main(String[] args) throws Exception {
String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
List<String> list = Arrays.asList(strArray);
list.stream().filter(f -> f.length()>3)
.sorted((a,b) -> b.compareTo(a))
.map(v ->v.toUpperCase())
.distinct()
.limit(3)
.peek(p -> p.toLowerCase())
.forEach(t -> System.out.println(t));
}
}
peek會對每個元素執行操作,並返回包含原stream元素的新Stream,什麼意思呢?先看結果:
REACT
PYTHON
NODE
並不是我們看到的小寫,因為peek產生的新的stream並不是我們已開始處理的Stream,所以我們看到的還是大寫。如果你的處理過程中涉及一些額外邏輯,但不影響最終結果,那麼你可以使用peek去搞一個新的Stream去處理。
7.總結
我們主要使用的是Intermediate 中的方法進行資料處理,Terminal 中的方法只能使用一個,這意味著對流的處理終止,這時才開始執行前面的那些Intermediate方法。最後對一些方法作一些解釋,就不一一演示了:forEach
遍歷、 forEachOrdered
按順序遍歷、 toArray
結果轉換成陣列、 reduce
結果中的元素進行組合、 collect
結果轉換成集合、 min
結果中最小值、 max
結果中最大值、 count
結果中元素數量、 anyMatch
結果中存在元素滿足某一條件、 allMatch
結果中所有元素都滿足某一條件、 noneMatch
結果中所有元素都不滿足某一條件、 findFirst
結果中第一條資料 、 findAny
結果中的任意一條資料、 iterator
遍歷