手把手帶你體驗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流分為三步:

  1. 創建Stream流
  2. 通過Stream流對象執行中間操作
  3. 執行最終操作,得到結果

三步走

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接口的方法

最后

這篇文章主要跟大家一起初步認識一下Stream流,至於中間操作、最終操作的API講解我就不寫了(網上的教程也很多)

使用Stream的原因我認為有兩個:

  1. JDK庫提供現有的API,代碼寫起來簡潔優化
  2. 方便並發。大家可以記住一個結論:在多核情況下,可以使用並行Stream API來發揮多核優勢。在單核的情況下,我們自己寫的for性能不比Stream API 差多少

參考資料:

樂於輸出干貨的Java技術公眾號:Java3y。公眾號內有200多篇原創技術文章、海量視頻資源、精美腦圖,關注即可獲取!

轉發到朋友圈是對我最大的支持!

覺得我的文章寫得不錯,點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM