手把手帶你體驗Stream流
前言
只有光頭才能變強。
文字已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongFuCheng3y/3y
上一篇講解到了Lambda表示式的使用《最近學到的Lambda表示式基礎知識》,還沒看的同學可以先去閱讀一下哈~
相信也有不少的同學想要知道:Lambda表示式在工作中哪個場景會用得比較多?跟Lambda搭邊的,使用Stream流會比較多
一般人第一次看Stream流的程式碼,都會有點看不懂(它的程式碼看起來好像就不是寫Java一樣.),希望這篇文章能帶大家入個門
一、體驗Stream流
大家在自學時,大多數會學過一個程式:算出從陣列元素的和
,當時我們是怎麼寫的?一般來說是這樣的:
public static void main(String[] args) {
int[] nums = { 1, 2, 3 };
int sum = 0;
for (int i : nums) {
sum += i;
}
System.out.println("結果為:" + sum);
}
如果我們使用Stream流的話,可以這樣:
public static void main(String[] args) { int[] nums = { 1, 2, 3 }; int sum2 = IntStream.of(nums).sum(); System.out.println("結果為:" + sum2); }
從程式碼量上可以明顯看出,用Stream流的方式會少一些。
我理解的Stream流程式設計就是:某些場景會經常用到操作(求和/去重/過濾....等等),已經封裝好API給你了,你自己別寫了,調我給你提供的API就好了。
1.1 支援併發
回到我們最原始的程式碼:
public static void main(String[] args) { int[] nums = { 1, 2, 3 }; int sum = 0; for (int i : nums) { sum += i; } System.out.println("結果為:" + sum); }
如果我們想要for
迴圈的內部支援併發的話,顯然不太好去寫。但使用Stream流的方式,呼叫一個方法就可以支援併發(parallel):
public static void main(String[] args) {
int[] nums = { 1, 2, 3 };
int sum2 = IntStream.of(nums).parallel().sum();
System.out.println("結果為:" + sum2);
}
優點:調API肯定是比自己寫的程式碼量要少。
缺點:不太方便除錯
為什麼要使用Stream流在我看來就是以上兩個原因:
- 方便併發
- 程式碼量少(直接呼叫API)
二、如何使用Stream流?
使用Stream流分為三步:
- 建立Stream流
- 通過Stream流物件執行中間操作
- 執行最終操作,得到結果
2.1 建立流
建立流我們最常用的就是從集合中創建出流
/**
* 返回的都是流物件
* @param args
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 從集合建立
Stream<String> stream = list.stream();
Stream<String> stream1 = list.parallelStream();
// 從陣列建立
IntStream stream2 = Arrays.stream(new int[]{2, 3, 5});
// 建立數字流
IntStream intStream = IntStream.of(1, 2, 3);
// 使用random建立
IntStream limit = new Random().ints().limit(10);
}
2.2 執行中間操作
怎麼理解中間操作?意思是這樣的:在上面我們已經能創建出Stream了,我們是對Stream進行操作,對Stream操作返回完返回的還是Stream,那麼我們稱這個操作為中間操作。
比如,我們現在有個字串my name is 007
,程式碼如下:
String str = "my name is 007";
Stream.of(str.split(" ")).filter(s -> s.length() > 2)
.map(s -> s.length()).forEach(System.out::println);
分解:
1、從字串陣列創建出流物件:
Stream<String> split = Stream.of(str.split(" "));
2、通過流物件的API執行中間操作(filter),返回的還是流物件:
Stream<String> filterStream = split.filter(s -> s.length() > 2);
3、通過返回的流物件再執行中間操作(map),返回的還是流物件:
Stream<Integer> integerStream = filterStream.map(s -> s.length());
因為中間操作返回的都是流物件,所以我們可以鏈式呼叫。
注意:Stream上的操作並不會立即執行,只有等到使用者真正需要結果的時候才會執行(惰性求值)。
比如說,peek()
是一箇中間操作,返回的是Stream流物件,只要它不執行最終的操作,這個Stream是不會執行的。
String str = "my name is 007";
Stream.of(str.split(" ")).peek(System.out::println); // 不會有資訊列印
2.3 執行最終操作
最終操作返回的不再是Stream物件,呼叫了最終操作的方法,Stream才會執行。還是以上面的例子為例:
String str = "my name is 007";
Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println)
這次我們加入了最終操作,所以這次的Stream流會被執行,由於中間操作和最終操作都是執行列印,所以會看到兩次列印:
至於中間操作和最終操作怎麼區分,我們以返回值來看就行了。中間操作返回的是Stream例項物件,最終操作返回的不是Stream例項物件:
最後
這篇文章主要跟大家一起初步認識一下Stream流,至於中間操作、最終操作的API講解我就不寫了(網上的教程也很多)
使用Stream的原因我認為有兩個:
- JDK庫提供現有的API,程式碼寫起來簡潔優化
- 方便併發。大家可以記住一個結論:在多核情況下,可以使用並行Stream API來發揮多核優勢。在單核的情況下,我們自己寫的
for
效能不比Stream API 差多少
參考資料:
- Java8中的流操作-基本使用&效能測試
- Java 8的Stream程式碼,你能看懂嗎?
樂於輸出乾貨的Java技術公眾號:Java3y。公眾號內有200多篇原創技術文章、海量視訊資源、精美腦圖,關注即可獲取!
覺得我的文章寫得不錯,點贊