天天看點

探索Java8 Stream源碼的核心秘密

作者:程式猿不相信眼淚
探索Java8 Stream源碼的核心秘密

核心回顧

stream 是一次性的,不是資料結構,不存儲資料,不改變源資料.。 API 分為終端和中間操作,中間操作是惰性的,碰到終端才去執行。 中間操作有無狀态和有狀态之分,有狀态需要更改上一步操作獲得的所有元素,才可以進行下一步操作,比如 排序 sorted,去重 distinct,跳過 skip,限制 limit 這四個,需要多疊代一次。 終端操作有短路與否之分,短路操作有 anyMatch, allMatch, noneMatch, findFirst, findAny

整體概覽

這裡列出一些重要的類,是看源碼過程中必須了解的。

探索Java8 Stream源碼的核心秘密

比如 :

  • Sink 接口是資料實際操作的地方,核心方法: begin,accept,end
  • AbstractPipeline 抽象類是核心的資料結構,雙連結清單

demo代碼

這裡沿用上文的例子

Student aStud = new Student(1, "a");
        Student bStud = new Student(2, "b");
        Student cStud = new Student(3, null);

//         集合的建立 一
        List<Student> collect1 = Stream.of(aStud, bStud, cStud).collect(Collectors.toList());
        collect1.forEach(System.out::println);
​
        List<String> studNameList = studentList.stream()
                .map(Student::getName)
                .filter(Objects::nonNull)
                .map(String::toUpperCase)
                .sorted()
                .map(e -> e + "c")
                .collect(Collectors.toList());
複制代碼           

步驟解析

都在這裡了

探索Java8 Stream源碼的核心秘密

這裡步驟太多了,就不一一放出來了 ,列下核心

  1. wrapSink, 建立 Sink 鍊,将管道的 Sink 操作連接配接在一起
  2. copyInto , 處理資料

wrapSink()

開始套娃,從 ReducingSink 往前套

探索Java8 Stream源碼的核心秘密

opWarpSink 方法調用的是每一步 中間操作 中的方法

探索Java8 Stream源碼的核心秘密

通過 單連結清單 的形式将他們聯系在一起

探索Java8 Stream源碼的核心秘密

Sink鍊建立結果

探索Java8 Stream源碼的核心秘密

copyInto()

這裡判斷是不是 短路操作 ,然後就去執行 Sink 的 begin,accept,end 方法。

通過 forEachRemaining 進行内部疊代,這個是 Spliterator 的方法。

探索Java8 Stream源碼的核心秘密

map 鍊節點,直接調用傳進來的方法,

探索Java8 Stream源碼的核心秘密

filter 鍊節點,多一步判斷

探索Java8 Stream源碼的核心秘密

sorted 節點,添加到 list 中。

注意,此時沒有繼續調用 downstream.accept 方法!

意味着,我們代碼中的 5 個中間步驟隻執行了前 3 個。

探索Java8 Stream源碼的核心秘密

不過别擔心, sorted 鍊節點中它重寫了這個 end,并開啟對新資料的新一輪周遊!

這就是我們提到的,有狀态中間操作多一次疊代的原因
探索Java8 Stream源碼的核心秘密

最後呢,是來到終止操作 TerminalOp 中的 accept,這裡執行的是 list 的 add 方法(我們調用 Collectors.toList() 中建構的),至此,資料添加到 state 中

探索Java8 Stream源碼的核心秘密
探索Java8 Stream源碼的核心秘密

擷取資料,ReducingSink 繼承了 Box 這個抽象類,最後 get 方法得到結果。

探索Java8 Stream源碼的核心秘密

總結

代碼對應的執行流程

  1. 先建立流,出現了 Head 節點
  2. 建立中間管道 Pipeline
  3. 調用終端操作後有三步
  4. (一)将中間管道的 Sink 操作連接配接在一起 (wrapSink) (二)處理資料 (copyInto),主要調用 Sink 中的 begin,accept(核心),end 操作 (三)傳回結果,ReducingSink 中的 get 方法
探索Java8 Stream源碼的核心秘密

主要記住這個 wrapSink 方法 和 copyInto 方法。

一個套娃,一個調用 begin,accept,end 等方法。

那麼,這個 stream 的原理機制就出來了:

利用 wrapSink 方法将各個中間操作中的 Sink 嵌套在一起,然後來到 copyInto 方法,調用 begin 通知各個 Sink 做好準備,接着進行内部疊代調用 accept 方法,再調用 end 方法完成資料的操作,最後通過 get 方法,擷取新容器中的資料,便是結果了。

此外,源碼的 鍊式調用API 寫法 ,設計模式 的使用以及 泛型 ,四大函數式接口 組合建構的高度抽象,封裝寫法,對我們的編碼能力,源碼閱讀能力也有很大的幫助!

比如 這個 Consumer+Function 接口的組合,配合泛型上下限的使用

探索Java8 Stream源碼的核心秘密

源碼中 通路者模式,工廠模式 等設計模式的影子

通路者模式: 将資料結構與資料操作分離。

對應源碼:資料結構是 Pipeline ,操作是 Sink

探索Java8 Stream源碼的核心秘密

對 stream 的特點更加熟悉

比如:

  1. stream 是一次性的,不是資料結構,不存儲資料,不改變源資料.。
  2. 中間操作是惰性的,遇到 終端操作才真正執行
  3. 有狀态的中間操作的特殊之處在于多疊代一次
  4. 内部疊代
  5. 終端操作主要做了兩件事,串連中間操作,調用 accept 方法處理資料

當然這裡我也隻測試了 非短路模式,短路模式又是怎樣設計的呢,小夥伴們可以自己上手 debug 下,加深印象。

原文連結:https://juejin.cn/post/7168268816441901087

來源:稀土掘金