Java?8中?Stream小知識小技巧方法梳理
前言
上篇只是簡單的動手操作操作了流(stream),那 stream 到底是什么呢?
官方的簡短定義:“從支持數(shù)據(jù)處理操作的源生成的元素序列”
分成三部分:
- 元素序列:你可以簡單將它類比于一樣,不過集合說的是數(shù)據(jù)的集合,而 stream 重點在于表達計算。如我們之前說到的 filter、map、sorted、limit等等
- 源:昨天我提到,如果了解過 Liunx 管道命令的朋友們,會知道,Liunx 的管道命令中的前一條命令的結(jié)果(輸出流)就是執(zhí)行下一條命令的輸入流。 stream流其實也是類似的,每次執(zhí)行完一個 filter、sorted 等等它們的返回值也是 stream 流,然后作為下一個操作的輸入流,這種。
- 數(shù)據(jù)處理:這里說的處理,就是那些filter、map、sorted、limit等。
你可以把它理解為概念上或是形式上的數(shù)據(jù)結(jié)構(gòu),而不是具體的。
只能遍歷的一次 Stream
Stream 流和 迭代器一樣,它只能夠迭代一次。
當它遍歷完的時候,我們就稱它已經(jīng)消費完了。如果還想重新執(zhí)行操作,那么就只能從原來的地方再獲取一個流啦。
?/**
? ? ? * 圖個樂子
? ? ? */
?public static List<String> list = Arrays.asList("醫(yī)醫(yī)","一一","花花","菡菡","春春","寧姐","...");
??
?public static void main(String[] args) {
? ? ?Stream<String> stream = list.stream();
? ? ?stream.forEach(System.out::println);
? ? ?stream.forEach(System.out::println);
?}當執(zhí)行第二句stream.forEach(System.out::println);時,會爆出如下異常:
?Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed ? at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279) ? at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) ? at com.nzc.stream02.Stream02Demo.main(Stream02Demo.java:25)
stream has already been operated upon or closed :流已經(jīng)被操作完或者關(guān)閉啦~
那么為什么流只能遍歷一次呢?
我的回答肯定是 Java 中的設(shè)計是如此。
我們先來回憶一下我昨天沒提到的一個知識點:
隨便舉一個例子,然后我們將 filter、map、limit、collect 都使用上。
?List<Student> students = new ArrayList<>();
??
?students.add(new Student("學生A", "大學1", 18, 98.0));
?students.add(new Student("學生A", "大學1", 18, 91.0));
?students.add(new Student("學生A", "大學1", 18, 90.0));
?students.add(new Student("學生A", "大學1", 18, 76.0));
?students.add(new Student("學生B", "大學1", 18, 91.0));
?students.add(new Student("學生C", "大學1", 19, 65.0));
?students.add(new Student("學生D", "大學2", 20, 80.0));
?students.add(new Student("學生E", "大學2", 21, 78.0));
?students.add(new Student("學生F", "大學2", 20, 67.0));
?students.add(new Student("學生G", "大學3", 22, 87.0));
?students.add(new Student("學生H", "大學3", 23, 79.0));
?students.add(new Student("學生I", "大學3", 19, 92.0));
?students.add(new Student("學生J", "大學4", 20, 84.0));
?List<String> collect = students.stream().filter(student -> student.getAge() > 20).map(student -> student.getName()).limit(3).collect(Collectors.toList());昨天我沒有具體說明這幾步是如何執(zhí)行的。
畫張圖來簡要的說明一下:

- filter, 根據(jù)條件篩選掉一些元素
- map,將元素轉(zhuǎn)換成其他的形式
- limit,將流進行截斷,只獲取其中的一部分
- collect,這里主要就是將流轉(zhuǎn)換成其他的集合對象,這一步也是流的終端操作,之后也會聊到。
在這里你可以看到Java 8中的 stream 的設(shè)計,就是只能遍歷一次,并且當執(zhí)行完終端操作的時候,流也是已經(jīng)關(guān)閉或者說是消費完了。
流操作
上面剛剛提到了終端操作,這也是一個小小的知識點。
Stream 的api提供了不少方法,諸如 filter、sotred、limit、map、collect等。
其中 filter、sotred、limit、map 它們的返回結(jié)果仍然是一個stream 流,這些操作,我們稱之為中間操作。并且它們之間的連接可以形成一個流水線任務(wù)。
而 collect 則是一個終端操作,它會觸發(fā)并執(zhí)行流水線任務(wù),最終關(guān)閉它。
中間操作
就是上面說的 filter、map等等,但是需要注意的是,如果流水線上沒有一個終端操作,那么你寫的filter、map什么的,并不會執(zhí)行任何處理。
通過一個簡單的案例,來看一看stream流中的lambda的函數(shù)的執(zhí)行流程。
? ? ?static List<Student> students = new ArrayList<>();
??
? ? ?static {
? ? ? ? ?students.add(new Student("學生A", "大學1", 18, 98.0));
? ? ? ? ?students.add(new Student("學生A", "大學1", 18, 91.0));
? ? ? ? ?students.add(new Student("學生A", "大學1", 18, 90.0));
? ? ? ? ?students.add(new Student("學生A", "大學1", 18, 76.0));
? ? ? ? ?students.add(new Student("學生B", "大學1", 18, 91.0));
? ? ? ? ?students.add(new Student("學生C", "大學1", 19, 65.0));
? ? ? ? ?students.add(new Student("學生D", "大學2", 20, 80.0));
? ? ? ? ?students.add(new Student("學生E", "大學2", 21, 78.0));
? ? ? ? ?students.add(new Student("學生F", "大學2", 20, 67.0));
? ? ? ? ?students.add(new Student("學生G", "大學3", 22, 87.0));
? ? ? ? ?students.add(new Student("學生H", "大學3", 23, 79.0));
? ? ? ? ?students.add(new Student("學生I", "大學3", 19, 92.0));
? ? ? ? ?students.add(new Student("學生J", "大學4", 20, 84.0));
? ? }?public static void main(String[] args) {
? ? ?List<String> collect = students.stream().filter(student -> {
? ? ? ? ?System.out.println("filter==>" + student.getName() + ":" + student.getAge());
? ? ? ? ?return student.getAge() > 20;
? ? }).map(student -> {
? ? ? ? ?System.out.println("map==>" + student.getName() + ":" + student.getAge());
? ? ? ? ?return student.getName();
? ? }).collect(Collectors.toList());
??
?}輸出:
filter==>學生A:18
map==>學生A:18
filter==>學生A:18
map==>學生A:18
filter==>學生A:18
map==>學生A:18
filter==>學生A:18
map==>學生A:18
filter==>學生B:18
map==>學生B:18
filter==>學生C:19
map==>學生C:19
filter==>學生D:20
filter==>學生E:21
filter==>學生F:20
filter==>學生G:22
filter==>學生H:23
filter==>學生I:19
map==>學生I:19
filter==>學生J:20
也許到這里還看的不明顯,我們再加上一個 limit(3),來看一下這次會如何輸出
?List<String> collect = students.stream().filter(student -> {
? ? ?System.out.println("filter==>" + student.getName() + ":" + student.getAge());
? ? ?return student.getAge() < 20;
?}).map(student -> {
? ? ?System.out.println("map==>" + student.getName() + ":" + student.getAge());
? ? ?return student.getName();
?}).limit(3).collect(Collectors.toList());
??
?System.out.println(collect);輸出結(jié)果:
?filter==>學生A:18 ?map==>學生A:18 ?filter==>學生A:18 ?map==>學生A:18 ?filter==>學生A:18 ?map==>學生A:18 ?[學生A, 學生A, 學生A]
1、實際上這里利用到的 limit(n) 就是俗稱的短路技巧,像我們剛學 &&、| | 語法時一樣,如果&&條件為false,則會短路,不執(zhí)行&& 之后的表達式,這里的 limit 你可以看作是 || 的表達。
Stream 流在內(nèi)部迭代中主動幫我們進行了優(yōu)化。
2、另外看似 filter 和 map 是兩個獨立的函數(shù),但實際上他們都是在一次遍歷中所進行的。這一點從輸出中也可看出。
終端操作
終端操作,就是觸發(fā)并執(zhí)行流水線任務(wù),最終關(guān)閉流的操作。
像之前寫的collect(Collectors.toList()) 和 forEach(System.out::println) 都是終端操作。
終端操作其結(jié)果返回的都是流的值,collect返回各種各樣的集合啦,
forEach 返回的就是 void 值。
中間操作:
| 操 作 | 類型 | 結(jié)果 | 操作參數(shù) | |
|---|---|---|---|---|
| filter | 中間 | Stream | Predicate | T -> boolean |
| map | 中間 | Stream | Stream Function | T -> R |
| limit | 中間 | Stream | ||
| sorted | 中間 | Stream | Comparator | (T, T) -> int |
| distinct | 中間 | Stream |
終端操作:
| 操 作 | 類 型 | 目 的 |
|---|---|---|
| forEach | 終端 | 消費流中的每個元素并對其應(yīng)用 Lambda。這一操作返回 void |
| count | 終端 | 返回流中元素的個數(shù)。這一操作返回 long |
| collect | 終端 | 把流歸約成一個集合,比如 List、Map 甚至是 Integer。 |
到此這篇關(guān)于Java 8中 Stream小知識小技巧方法梳理的文章就介紹到這了,更多相關(guān)Java 8 Stream 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過FeignClient如何獲取文件流steam?is?close問題
這篇文章主要介紹了通過FeignClient如何獲取文件流steam?is?close問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
Java微信公眾平臺開發(fā)(13) 微信JSSDK中Config配置
這篇文章主要為大家詳細介紹了Java微信公眾平臺開發(fā)第十三步,微信JSSDK中Config配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
SpringBoot框架實現(xiàn)切換啟動開發(fā)環(huán)境和測試環(huán)境
這篇文章主要介紹了SpringBoot框架實現(xiàn)切換啟動開發(fā)環(huán)境和測試環(huán)境,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

