Java Stream原理

文章目录

    • 一、简介
    • 二、原理
      • 2.1 如何保存操作?
      • 2.2 如何形成流?
      • 2.3 如何执行?
    • 参考

一、简介

流操作可以分为中间操作和结束操作。

  • 中间操作分为:无状态操作(map、fliter、skip等等)和有状态操作(需要得到所有元素才能进行,例如limit、sorted、distinct)
  • 结束操作分为:短路操作(遇到符合条件的元素就终止,如findFirst、anyMatch、findAny)和非短路操作(需要得到所有元素才能进行,如collect、max、count、forEach等)

Stream的实现:

  1. 采用了流水线的方式,Stream中的每个操作都对应一个Pipeline对象,每个Pipeline对象都含有sourceStage、previousStage(upStream)、nextStage(downStream)三个属性。最终整个Stream操作会转化为一个Pipeline的双向链表。
  2. 首先stream()函数会产生一个Head,其不含任何操作,存储着原始数据。
  3. 接下来每个操作都会产生一个Pipeline对象,无状态操作对应StatelessOp对象,有状态操作对应StatefullOp对象。
  4. 每个Pipeline对象创建(除了Head对象)时,都会将this指针传入作为上源流,因为例如xxx.stream().map().filter().collect()这一串流操作,每一步都会创建一个新的Pipeline对象,然后下一步由上一步创建的新对象调用,this刚好指向上一步的对象,stream()会创造一个Head对象,然后Head对象再调用map创建一个StatelessOp对象,将this传入,this指向Head。
  5. 直到结束操作,结束操作会使用递归的方式不断调用onWrapSink函数,由下向上构建一个Sink链,然后执行流操作。所以所有的中间操作只是会被记录,只有遇到结束操作时,才会执行真正的所有操作。
  6. 结束操作对应TerminalOp,最终会执行evaluateSequential方法,从head开始依次执行。

类图结构:
Java Stream原理_第1张图片
执行过程(图是偷的别人的):
Java Stream原理_第2张图片

二、原理

2.1 如何保存操作?

流式操作将每一步都对应一个Pipeline对象(Stage),其由三部分组成,即<数据源,操作,回调函数>。

Pipeline虽然知道自己这一步应该做什么,但是不知道下一步应该做什么,比如xxx.stream().map().filter().collect(),map()函数创造的StatelessOp虽然知道我自己应该做的是根据传入的回调函数做map操作,但是不知道下一步应该是fliter(),所以在此之上多了一层封装,即将每个Stage都封装成一个Sink对象,每个Sink统一接口,当map对象的Sink执行完逻辑后,只需要调用下一个Sink的accpet()方法即可。

以集合类为例,首先看Collection的stream()函数,这是流的起点。

//Collection接口
  default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

//StreamSupport
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head<>(spliterator,
                                            StreamOpFlag.fromCharacteristics(spliterator),
                                            parallel);
    }

//可以看到创建了一个Head类,parallel参数判断是否是并行流
//此处先看单行流,spliterator是一个迭代器,即数据源
//为何使用迭代器呢?这是因为迭代器封装了数据遍历操作
//这样不管什么类型的数据,访问只需要调用统一的next()和hasNext()方法

最终调用了AbstractPipeline的构造方法:

AbstractPipeline(Spliterator<?> source,
                     int sourceFlags, boolean parallel) {
        this.previousStage = null; //没有上游,流的起点
        this.sourceSpliterator = source;//数据源
        this.sourceStage = this;
        this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
        // The following is an optimization of:
        // StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
        this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
        this.depth = 0;
        this.parallel = parallel;
    }

然后通过stream()方法得到一个Head对象,我们再调用Head()对象的map()方法,则又会返回一个新的StatelessOp对象,并且其previousStage指向Head。

因为Head是ReferencePipeline的子类,且没有重写map()、fliter()等等的方法,所有我们看ReferencePipeline的map()方法:

    @Override
    @SuppressWarnings("unchecked")
    public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
        Objects.requireNonNull(mapper);
        //创建了一个新的StatelessOp对象,并返回了
        return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
            //该方法是核心,将Pipeline的逻辑封装为一个Sink并串连
            @Override
            Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
                return new Sink.ChainedReference<P_OUT, R>(sink) {
                    @Override
                    public void accept(P_OUT u) {
                        downstream.accept(mapper.apply(u));
                    }
                };
            }
        };
    }

这样不断的进行,就可以形成一个从下游到上游的单向链表。即:
Head<—StatelessOp(map)<—… <—TerminalOp(collect)

结束操作会通过ReduceOps、MatchOps等工具类的makeRef()方法构造一个TerminalOp。

2.2 如何形成流?

前面流已经完成了从下至上的连接,但是还无法执行,因为是逆序的。

如何来完成从上游到下游的连接? 这一切从TerminalOp的evaluateSequential()开始。

当我们调用了结束方法(anyMatch、collect等等)后,会调用ReferencePipeline的evaluate(TerminalOp)方法,该方法会完成由上到下的连接。

evaluate方法会根据并行与否执行terminalOp.evaluateParallel或者terminalOp.evaluateSequential方法。

以evaluateSequential为例:该方法在不同的结束操作中有不同的实现,以ReduceOps(collect()底层使用该结束操作类)为例。

//此处helper为evaluateSequential()将this作为参数传入,
//因为terminalOp实现了PipelineHelper接口
 @Override
        public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
                                           Spliterator<P_IN> spliterator) {
            return helper.wrapAndCopyInto(makeSink(), spliterator).get();
        }

//而terminalOp实现的该接口是继承自父类AbstractPipeline
//所以最终wrapAndCopyInto是来自AbstractPipeline
//AbstractPipeline类
@Override
    final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
        copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
        return sink;
    }


最终逻辑就定义在wrapSink函数中:

 @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) {
        //递归调用onWrapSink函数,将自己作为downstream参数传入
        //p指向当前sink的previousStage,即upstream
            sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
        }
        return (Sink<P_IN>) sink;
    }

//最后返回最顶层的Sink

这样就完成了由上游到下游的连接,调用的时候(copyInto()函数),我们看每个Sink的几个方法中,肯定会包含downstream.accept()的调用。这样就可以从Sink执行完毕后,将数据传入下一个Sink,进行接下来的处理。

2.3 如何执行?

注意数据源是在:
terminalOp.evaluateSequential(this,sourceSpliterator(terminalOp.getOpFlags()));作为参数传入的

//copyInto()函数
@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);
        }
    }

数据处理由spliterator.forEachRemaining(wrappedSink)语句完成。

//spliterator接口
 default void forEachRemaining(Consumer<? super T> action) {
        do { } while (tryAdvance(action));
    }

//tryAdvance函数由具体实现类去实现
//这里,由于我们是使用容器Collection产生的流,
//所以spliterator的具体类型为IteratorSpliterator

//回顾前面Collection在调用stream()函数时,调用了一次spliterator()
//该函数调用Spliterators.spliterator(this, 0)生成一个IteratorSpliterator

由于Collection产生的流所对应的spliterator的具体实现类为IteratorSpliterator,我们看IteratorSpliterator的tryAdvance()方法:

    @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            if (action == null) throw new NullPointerException();
            if (it == null) {
                it = collection.iterator();
                est = (long) collection.size();
            }
            if (it.hasNext()) {
                //调用顶层Sink的accpet,然后由于每个Sink
                //在accept函数中都明确调用downstream.accpet()
                //因此完成了流式处理,最终返回结果
                action.accept(it.next());
                return true;
            }
            return false;
        }

参考

  1. jdk源码
  2. Java8 Stream原理深度解析
  3. 深入理解Java8中Stream的实现原理

你可能感兴趣的:(#,java基础,java,stream)