一、操作分类
Stream 流的出现主要是为了简化迭代的操作,提高迭代效率。
简单解释一下表格中加粗字体的含义:
- 中间操作:从字面上看是流从开始到结束中间的一环,从操作结果上看是上一个流经过中间操作生成了下一个流,从代码上看实际上是生成了一个个 stage 和 sink节点,至于 sink 节点是什么,后面的篇幅会分析
- 结束操作:和中间操作相反,结束操作之后是流的最后一个环节,不再生成新的流,从代码上看是启动整个 sink 节点从头开始运行
- 有状态:简单理解就是需要获取流中所有数据之后才能进行的操作,比如 sort 排序,肯定要获取了所有的元素之后才能排序
- 无状态:不需要获取所有元素,只需要对单个元素进行的操作,比如 filter 过滤,只针对每一个元素本身所做的操作
- 短路:终结操作中只要有一个元素可以得到操作结果的数据,比如 findAnyMatch,只要有一个节点满足筛选条件就会返回 true
- 非短路:需要遍历所有的节点才能得到预期结果,比如 forEach、max
二、Stream流原理探究
2.1 重要的接口类
BaseStream
最顶端的接口类,定义了流的基本接口方法,最主要的方法为 spliterator、isParallel。
Stream
最顶端的接口类。定义了流的常用方法,例如 map、filter、sorted、limit、skip、collect 等。
ReferencePipeline
ReferencePipeline 是一个结构类,定义内部类组装了各种操作流,定义了Head、StatelessOp、StatefulOp三个内部类,实现了 BaseStream 与 Stream 的接口方法。
Sink
Sink 接口定义了 Stream 之间的操作行为,包含 begin()、end()、cancellationRequested()、accpt()四个方法。ReferencePipeline最终会将整个 Stream 流操作组装成一个调用链,而这条调用链上的各个 Stream 操作的上下关系就是通过 Sink 接口协议来定义实现的。
2.2 类图
首先看一下类的继承关系图:
源码中,以 Stage 来描述每个阶段,而且是典型的双向链表结构。因此 Stream 流过程可以用下图描述
流程图
2.3 实例分析
list.stream().filter(integer -> integer>5).sorted().max();
2.3.1 生成 Head 对象
第一步是 stream 方法,最终调用到下面的代码,可以看出,调用完 stream 方法后本质上生成了一个 Head 对象
public final class StreamSupport {
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
//生成 Head
return new ReferencePipeline.Head<>(spliterator, StreamOpFlag.fromCharacteristics(spliterator),
parallel);
}
}
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
AbstractPipeline(Spliterator<?> source,
int sourceFlags, boolean parallel) {
// 上游 Stage,Head 上游为 null
this.previousStage = null;
// 源分裂器,可以理解成高级版本的迭代器
this.sourceSpliterator = source;
// Head 指针,指向自己
this.sourceStage = this;
//是否是中间操作的标志
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
// The following is an optimization of:
//源以及所有操作的组合源的操作标志,包括此管道对象表示的操作。在评估管道准备时有效。
this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
//此管道对象与流源(如果是顺序)之间的中间操作数,Head 的 depth 为 0
this.depth = 0;
//如果管道是并行的,则为真,否则管道是顺序的;仅对源阶段有效.
this.parallel = parallel;
}
}
2.3.2. 生成 filter 对应的操作对象
第二步是 filter 方法,最终调用如下,创建了一个无状态的操作对象 StatelessOp,创建对象时会将 Head (this) 对象传入,构建双向链表关系,同时也会记录 sourceStage (Head) 。至此,上图的指向关系已经明了。
@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
Objects.requireNonNull(predicate);
//这里的 this 是 Head 对象 ,最终会被当前 StatelessOp 对象作为 previousStage 记录,同时也会将 Head 对象的 nextStage 指向 StatelessOp
return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
//过滤的实现
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if (previousStage.linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
//将 Head 的 nextStage 指针指向当前对象,即封装了 filter 的 StatelessOp 对象
previousStage.nextStage = this;
//将 StatelessOp 对象的 previousStage 指向 Head
this.previousStage = previousStage;
this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
//将 StatelessOp 对象的 sourceStage 指向 Head
this.sourceStage = previousStage.sourceStage;
if (opIsStateful())
sourceStage.sourceAnyStateful = true;
this.depth = previousStage.depth + 1;
}
这里出现了 Sink 接口和 Sink 接口的实现类 ChainedReference ,Sink 接口是用来联系当前流和下一个流的协议接口,每个 AbstractPipeline 的具体子类都要实现 opWrapSink 方法返回一个 Sink 实例。
interface Sink<T> extends Consumer<T> {
//开始方法,可以通知下游做些相关准备
default void begin(long size) {}
//结束方法,告诉下游已经完成所有元素的迭代操作
default void end() {}
//是否是短路操作
default boolean cancellationRequested() {
return false;
}
//Sink 接口的默认实现类
static abstract class ChainedReference<T, E_OUT> implements Sink<T> {
//此处的 downstream 意指下游的处理 sink
protected final Sink<? super E_OUT> downstream;
public ChainedReference(Sink<? super E_OUT> downstream) {
this.downstream = Objects.requireNonNull(downstream);
}
@Override
//默认直接调用下游的 begin 方法
public void begin(long size) {
downstream.begin(size);
}
@Override
//默认直接调用下游的 end 方法
public void end() {
downstream.end();
}
@Override
//返回是否短路
public boolean cancellationRequested() {
return downstream.cancellationRequested();
}
}
}
2.3.3. 生成 sorted 对应的对象
第三步的 sorted 方法,sorted 返回的是 OfRef 类的一个实例,类似 filter ,同样也实现了 opWrapSink 方法,不过因为 sorted 方法实现的是排序功能,所以这里会确定比较器 comparator,最后由比较器实现排序逻辑。
private static final class OfRef<T> extends ReferencePipeline.StatefulOp<T, T> {
OfRef(AbstractPipeline<?, T, ?> upstream) {
//类似 filter 方法,确定上下游 stage 关系
super(upstream, StreamShape.REFERENCE,
StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
this.isNaturalSort = true;
@SuppressWarnings("unchecked")
//默认比较器
Comparator<? super T> comp = (Comparator<? super T>) Comparator.naturalOrder();
this.comparator = comp;
}
//...
@Override
public Sink<T> opWrapSink(int flags, Sink<T> sink) {
Objects.requireNonNull(sink);
if (StreamOpFlag.SORTED.isKnown(flags) && isNaturalSort)
return sink;
else if (StreamOpFlag.SIZED.isKnown(flags))
return new SizedRefSortingSink<>(sink, comparator);
else
//返回的是 RefSortingSink 实例
return new RefSortingSink<>(sink, comparator);
}
}
private static final class RefSortingSink<T> extends AbstractRefSortingSink<T> {
private ArrayList<T> list;
RefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) {
super(sink, comparator);
}
@Override
//开始操作,初始化一个 list,用于接收上游流过来的元素,进行排序
public void begin(long size) {
if (size >= Nodes.MAX_ARRAY_SIZE)
throw new IllegalArgumentException(Nodes.BAD_SIZE);
list = (size >= 0) ? new ArrayList<T>((int) size) : new ArrayList<T>();
}
@Override
//实际的排序操作
public void end() {
list.sort(comparator);
downstream.begin(list.size());
//终结操作不是短路操作,遍历
if (!cancellationWasRequested) {
list.forEach(downstream::accept);
}
//终结操作是短路操作,找到满足的元素
else {
for (T t : list) {
if (downstream.cancellationRequested()) break;
downstream.accept(t);
}
}
//结束
downstream.end();
list = null;
}
@Override
//添加元素到初始化好的 list 中
public void accept(T t) {
list.add(t);
}
}
2.3.4. 拨动齿轮
至此为止,我们已经构建好了双向链表的操作,每个操作都被保存到一个个 StatelessOp 或者 StatefulOp 对象中,但在之前的操作中,我们封装好的 Sink 对象并没有实际调用,这也是为什么 Stream 流如果不进行终结操作之前的中间操作都不会触发的原因,万事俱备,只欠东风。东风就是终结操作。最后为 max(Comparator.naturalOrder()),是终结操作,会生成一个最终的 Stage,通过这个 Stage 触发之前的中间操作,从最后一个Stage开始,递归产生一个Sink链。
1)java.util.stream.ReferencePipeline#max
@Override
public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) {
return reduce(BinaryOperator.maxBy(comparator));
}
2)最终调用到 java.util.stream.AbstractPipeline#wrapSink,这个方法会调用 opWrapSink 生成一个 Sink 链表,对应到本文的例子,就是 filter 和 map 操作。
@Override
@SuppressWarnings("unchecked")
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink);
for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}
return (Sink<P_IN>) sink;
}
3)wrapAndCopyInto 生成 Sink 链表后,会通过 copyInfo 方法执行 Sink 链表的具体操作。
@Override
final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
Objects.requireNonNull(wrappedSink);
if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
wrappedSink.begin(spliterator.getExactSizeIfKnown());
spliterator.forEachRemaining(wrappedSink);
wrappedSink.end();
}
else {
copyIntoWithCancel(wrappedSink, spliterator);
}
}
4)java.util.Spliterators.ArraySpliterator#forEachRemaining
@Override
public void forEachRemaining(Consumer<? super T> action) {
Object[] a; int i, hi; // hoist accesses and checks from loop
if (action == null)
throw new NullPointerException();
if ((a = array).length >= (hi = fence) &&
(i = index) >= 0 && i < (index = hi)) {
do { action.accept((T)a[i]); } while (++i < hi);
}
}
三、用面向对象的思维去理解 Stream 流
类比实际生活,我们可以将 Stream 流比作水流,中间操作则是相当于水流过程中的蓄水池,Sink则是每个蓄水池的操作者,终结操作则是下达指令的指挥官。此例中的操作可如下图:
四、Stream流中的责任链模式
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
sink的链表从头开始处理元素,处理完交给后面的sink结点继续处理。直到尾结点,使用了责任链模式
五、Stream流中使用的匿名内部类
Stream在创建中间处理结点时创建了Sink.ChainedReference抽象类的匿名内部类。匿名内部类的内部使用了外部变量predicate。匿名内部类在使用外部变量时,会自动生成构造函数,并使用该外部变量生成同类型的匿名内部类的成员变量。因此每次调用filter操作,实际上是调用opWrapSink操作,如果调用filter时使用函数a)生成predicate,其实该函数a不是在每个元素经过调用链时被调用,而是只有在调用链被初始化时调用一次。之后的每次filter都是使用匿名内部类的成员变量进行过滤。
@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
Objects.requireNonNull(predicate);
//这里的 this 是 Head 对象 ,最终会被当前 StatelessOp 对象作为 previousStage 记录,同时也会将 Head 对象的 nextStage 指向 StatelessOp
return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
//过滤的实现
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
参考:
Java Stream 源码分析
浅谈 jdk 中的 Stream 流使用及原理