浅谈 jdk 中的 Stream 流使用及原理

一、操作分类

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 流使用及原理

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值