思路
先上一段代码
List<User> userList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
userList.add(new User(i, "wtq", "1234"));
}
userList.stream()
.filter(user -> user.getUserId() > 3)
.filter(user -> user.getUserId() > 4)
.forEach(System.out::println);
Java的stream流, 平时很常用的功能, 让代码变的更简洁, 可读性也更好, 但是完全没有了解过它内部的逻辑. 今天有时间自己尝试梳理了一下源码, 这也算是我第一次独立分析源码, 写的不好还请见谅.
整体逻辑
先在前面总结一下整套流程的大体逻辑.
在学习stream的使用方法时, 就了解到一些流操作分为中间操作
和终止操作
. 中间操作
并不会触发逻辑执行, 真正触发操作执行是终止操作
.
一段流操作中的中间操作
在内部使用AbstractPipeline
被连接成双向链表
的结构, 并使用Sink
保存具体的操作方式(如上面的过滤逻辑).
直到有一个中间操作对象
调用了终止操作
.终止操作
需要实现TerminalOp
和Sink
. 由终止操作
构造Sink
调用单向链表. 最终将Sink
执行链委托给Spliterator
调用.
重要的类
AbstractPipeline : 双向链表元素类
Sink : 操作链, 串联整个流操作中的具体逻辑的类
Spliterator : 真正遍历调用操作链的类
这里暂时听不明白没关系, 下面正式进入代码
读代码
stream()
从第一个方法开始分析.
这个方法构造了最初的Stream
对象, 第一个参数是通过调用内部方法获取的.先不管这个参数.
点进StreamSupport
提供的方法, 可以发现这里的Stream
对象的实际类型是ReferencePipeline
的内部类Head
构造了双向链表的头.
filter() 中间操作
注意, 这里filter()其实是由Head
对象调用的, 注意搞清楚this
的值.
这里创建了一个StatelessOp
.
继续追溯构造方法, 实际是调用了AbstractPipeline
的构造方法, 此时构造对象的previousStage
就是Head
. 同时为previousStage
的nextStage
赋值. 这时候, 双向链表已经有了两个元素.
这里想象一下第二个filter运行时, 会将上一个filter的StatelessOp
对象作为previousStage
, 执行相同的操作. 做完这些操作, 双向链表里加入了第三个元素.
foreach() 终止操作
此时终止操作是由最后一个中间操作调用
这里将终止操作的逻辑保存在ForEachOps
对象中
由终止操作
调用最终评估方法, 将全部中间操作
构成的链表传入方法
继续传递
构造Sink
单向链表
将Sink
链表传递给Spliterator
为每个元素调用Sink
操作链