核心回顧
stream 是一次性的,不是資料結構,不存儲資料,不改變源資料.。 API 分為終端和中間操作,中間操作是惰性的,碰到終端才去執行。 中間操作有無狀态和有狀态之分,有狀态需要更改上一步操作獲得的所有元素,才可以進行下一步操作,比如 排序 sorted,去重 distinct,跳過 skip,限制 limit 這四個,需要多疊代一次。 終端操作有短路與否之分,短路操作有 anyMatch, allMatch, noneMatch, findFirst, findAny
整體概覽
這裡列出一些重要的類,是看源碼過程中必須了解的。
比如 :
- 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());
複制代碼
步驟解析
都在這裡了
這裡步驟太多了,就不一一放出來了 ,列下核心
- wrapSink, 建立 Sink 鍊,将管道的 Sink 操作連接配接在一起
- copyInto , 處理資料
wrapSink()
開始套娃,從 ReducingSink 往前套
opWarpSink 方法調用的是每一步 中間操作 中的方法
通過 單連結清單 的形式将他們聯系在一起
Sink鍊建立結果
copyInto()
這裡判斷是不是 短路操作 ,然後就去執行 Sink 的 begin,accept,end 方法。
通過 forEachRemaining 進行内部疊代,這個是 Spliterator 的方法。
map 鍊節點,直接調用傳進來的方法,
filter 鍊節點,多一步判斷
sorted 節點,添加到 list 中。
注意,此時沒有繼續調用 downstream.accept 方法!
意味着,我們代碼中的 5 個中間步驟隻執行了前 3 個。
不過别擔心, sorted 鍊節點中它重寫了這個 end,并開啟對新資料的新一輪周遊!
這就是我們提到的,有狀态中間操作多一次疊代的原因
最後呢,是來到終止操作 TerminalOp 中的 accept,這裡執行的是 list 的 add 方法(我們調用 Collectors.toList() 中建構的),至此,資料添加到 state 中
擷取資料,ReducingSink 繼承了 Box 這個抽象類,最後 get 方法得到結果。
總結
代碼對應的執行流程
- 先建立流,出現了 Head 節點
- 建立中間管道 Pipeline
- 調用終端操作後有三步
- (一)将中間管道的 Sink 操作連接配接在一起 (wrapSink) (二)處理資料 (copyInto),主要調用 Sink 中的 begin,accept(核心),end 操作 (三)傳回結果,ReducingSink 中的 get 方法
主要記住這個 wrapSink 方法 和 copyInto 方法。
一個套娃,一個調用 begin,accept,end 等方法。
那麼,這個 stream 的原理機制就出來了:
利用 wrapSink 方法将各個中間操作中的 Sink 嵌套在一起,然後來到 copyInto 方法,調用 begin 通知各個 Sink 做好準備,接着進行内部疊代調用 accept 方法,再調用 end 方法完成資料的操作,最後通過 get 方法,擷取新容器中的資料,便是結果了。
此外,源碼的 鍊式調用API 寫法 ,設計模式 的使用以及 泛型 ,四大函數式接口 組合建構的高度抽象,封裝寫法,對我們的編碼能力,源碼閱讀能力也有很大的幫助!
比如 這個 Consumer+Function 接口的組合,配合泛型上下限的使用
源碼中 通路者模式,工廠模式 等設計模式的影子
通路者模式: 将資料結構與資料操作分離。
對應源碼:資料結構是 Pipeline ,操作是 Sink
對 stream 的特點更加熟悉
比如:
- stream 是一次性的,不是資料結構,不存儲資料,不改變源資料.。
- 中間操作是惰性的,遇到 終端操作才真正執行
- 有狀态的中間操作的特殊之處在于多疊代一次
- 内部疊代
- 終端操作主要做了兩件事,串連中間操作,調用 accept 方法處理資料
當然這裡我也隻測試了 非短路模式,短路模式又是怎樣設計的呢,小夥伴們可以自己上手 debug 下,加深印象。
原文連結:https://juejin.cn/post/7168268816441901087
來源:稀土掘金