Java8流源码解析-串行流

概述

Stream类体系的设计主要采用的是双向链表数据结构,责任链和构建器设计模式。

Stream体系

流和中间阶段关系图

BaseStream:对一组可序列化的元素支持串行、并行的聚合操作。
Stream:对一组可序列化的元素支持串行、并行的聚合操作。
PipelineHelper:执行Stream流水线的帮助类,捕获一个Stream流水线的全部信息(输出形状,中间操作,流标记,是否并行等)。
AbstractPipeline:流水线的抽象基本类,提供了流接口的核心实现,管理构造和执行Stream流水线。
ReferencePipeline:对流水线的中间阶段或者源阶段处理的抽象类。
ReferencePipeline.Head:ReferencePipeline源阶段。
ReferencePipeline.StatefulOp:Stream的有状态中间阶段的基本抽象类。
ReferencePipeline.StatelessOp:Stream的无状态中间阶段的基本抽象类。

中间操作和流源都属于ReferencePipeline(即Stream)。
在这里插入图片描述

终端操作

TerminalOp:消费输入的流并产生输出结果。
Sink:Consumer类的扩展,通过流水线的阶段传导值,使用相关方法去管理信息大小、流程控制等。
TerminalSink:一个Sink,用于累积元素被消费的状态,当计算结束后允许去获取计算结果。
ForEachOp:一个终端操作,执行流式线和发送输出结果到自己,作为一个TerminalSink。
ReduceOp:一个终端操作,执行流式线和发送输出结果到AccumulatingSink,RedueceOp用来执行化简操作。
MatchOp:一个短路终端操作,流上的所有元素执行谓词操作,决定是否所有的、任何的、没有元素满足谓词判断。
FindOp:一个短路终端操作,搜索流上的元素,当发现时中断执行。
在这里插入图片描述

Sink

Sink:Consumer类的扩展,通过流水线的阶段传导值,使用相关方法去管理信息大小、流程控制等。
TerminalSink:一个Sink,用于累积元素被消费的状态,当计算结束后允许去获取计算结果。
FindSink:实现TerminalSink,具有元素发现、当发现目标后请求取消功能。
AccumulatingSink:TerminalSink的一种,实现流上的元素归约和返回计算结果。
Sink.ChainedReference:实现Sink的抽象类,用于创建Sink链。通过begin,end,cancellationRequest连接下游的水槽。
SortedOps.AbstractRefSortingSink:实现Sink的抽象类,实现引用流的排序。
SortedOps.RefSortingSink:实现引用流排序的Sink。
SortedOps.SizedRefSortingSink:实现指定大小的引用流排序的Sink。
在这里插入图片描述

Spliterator

拆分器,遍历或者拆分源数据。
拆分器可以用tryAdvance()单个的访问元素或者使用forEachRemaining顺序遍历元素。

Stream总结

Stream流都是获取一个数据源–》数据转换–》执行操作 获取想要的结果。每次转换原有Stream对象不变,返回一个新的stream对象,可以有多次转换,这就允许对流的操作可以向链条一样排列,变成一个流水线/管道(pipeline)。
Stream使用ReferencePipeline记录用户中间操作,它把每一个操作当成一个阶段,它把这个阶段分成三个类型:Head、StatefulOp和StatelessOp,Stream每调用一次操作其实就是生成一个新的阶段。这些阶段通过双向链表的形式组织串联在一起,建立起了阶段的叠加。
阶段叠加起来之后,Stream使用Sink机制把每个操作串联起来。Sink是封装在每个流阶段里面的,包含TerminalSink和ChainedReference,当终止操作执行的时候,内部封装一个accumulatingSink,终止阶段会从TerminalSink开始从下游往上游回溯,其实就是指针迁移,一层一层包装Sink,最终包装出一个Sink链,在Sink链路中,元素是通过accept方法进行发射传递的。构造完成Sink链路之后就可以对每个元素分别执行begin、accept、end操作完成内部迭代了。

流源构建(构建流水线的Head阶段)

Collection类里的stream()方法就是生成流源的:
Collection.java

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

StreamSupport.java

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

可以看到它最终是通过分割迭代器构建了一个流管道的源ReferencePileline.Head,是流水线的开始阶段。通过父类AbstractPipeline属性可以看出流的每个阶段是一个双向链表的节点,而每个阶段都可以反向链接回源阶段。

abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
        extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
    private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed";
    private static final String MSG_CONSUMED = "source already consumed or closed";

    /**
     * Backlink to the head of the pipeline chain (self if this is the source
     * stage).
     */
    @SuppressWarnings("rawtypes")
    private final AbstractPipeline sourceStage;

    /**
     * The "upstream" pipeline, or null if this is the source stage.
     */
    @SuppressWarnings("rawtypes")
    private final AbstractPipeline previousStage;

    /**
     * The operation flags for the intermediate operation represented by this
     * pipeline object.
     */
    protected final int sourceOrOpFlags;

    /**
     * The next stage in the pipeline, or null if this is the last stage.
     * Effectively final at the point of linking to the next pipeline.
     */
    @SuppressWarnings("rawtypes")
    private AbstractPipeline nextStage;
    
	...
}

构建流源比重很大的一个参数是sourceSpliterator,这个迭代器Spliterator是jdk8引入的接口,类似于Iterator,但是它是可分割的,利用分而治之的思想,在并行流的时候可以利用多线程ForkJoin并行操作,而且每次处理集合元素时使用tryAdvance()或forEachRemaining()方法。
流源的构建负责封装原始数据,并初始化双向链表的数据结构。

中间操作阶段

以无状态的map操作阶段举例

ReferencePipeline.java

public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
    Objects.requireNonNull(mapper);
    return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
                                 StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
        @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));
                }
            };
        }
    };
}

方法构建了一个StatelessOp,它是无状态中间操作阶段的基类,该类创建一个双向链表的节点,并把之前的节点设置成上个操作阶段,刚刚创建的节点设置成下一个阶段。
AbstractPipeline.java

   AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
        if (previousStage.linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        previousStage.linkedOrConsumed = true;
        previousStage.nextStage = this;

        this.previousStage = previousStage;
        this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
        this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
        this.sourceStage = previousStage.sourceStage;
        if (opIsStateful())
            sourceStage.sourceAnyStateful = true;
        this.depth = previousStage.depth + 1;
    }

map方法中还实现了opWrapSink方法,构建了一个ChainedReference,该类用于创建Sink链节点,它接收一个位置输入类型的下游Sink(是Sink的一个抽象实现)。Sink是Consumer接口的一个扩展,用于传导管道上的元素。在第一次调用Sink的accept方法前,必须调用begin方法通知有多少数据已经到来,所有数据处理完毕,必须调用end方法,cancellationRequest方法用于取消接受数据。包装Sink的实质就是如何重载Sink的四个接口方法,实现数据的不断消费并发送。
map方法负责链接上游的阶段,形成双向链表的数据结构,内部封装一个Sink形成Sink操作链(Sink使用的是责任链的设计模式)用于执行map操作,然后把结果发送到下游Sink。

有状态的中间操作sorted

public final Stream<P_OUT> sorted() {
    return SortedOps.makeRef(this);
}

SortedOps.java

static <T> Stream<T> makeRef(AbstractPipeline<?, T, ?> upstream) {
    return new OfRef<>(upstream);
}

构建一个OfRef,它是StatefulOp的实现类,该类也会完成链表的连接。它的opWrapSink方法,生成了RefSortingSink:

    private static final class OfRef<T> extends ReferencePipeline.StatefulOp<T, T> {
        /**
         * Comparator used for sorting
         */
        private final boolean isNaturalSort;
        private final Comparator<? super T> comparator;

        /**
         * Sort using natural order of {@literal <T>} which must be
         * {@code Comparable}.
         */
        OfRef(AbstractPipeline<?, T, ?> upstream) {
            super(upstream, StreamShape.REFERENCE,
                  StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
            this.isNaturalSort = true;
            // Will throw CCE when we try to sort if T is not Comparable
            @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 the input is already naturally sorted and this operation
            // also naturally sorted then this is a no-op
            if (StreamOpFlag.SORTED.isKnown(flags) && isNaturalSort)
                return sink;
            else if (StreamOpFlag.SIZED.isKnown(flags))
                return new SizedRefSortingSink<>(sink, comparator);
            else
                return new RefSortingSink<>(sink, comparator);
        }
    }

RefSortingSink对Sink接口实现:
SortedOps.java

    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
        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
        public void accept(T t) {
            list.add(t);
        }
    }

begin初始化一个集合用于后续的动作,accept方法做做收集元素的动作,而排序交给end操作,因为到end说明所有数据接收完毕,同时end负责分发数据到下游,即有状态操作下游的begin,accept,end是放在end中操作的。
流源的中间操作都是在构建:构建流水线双向链表,构建Sink操作链,最好终止操作执行所有的内部迭代。

终止操作

终止操作collect

ReferencePipeline.java

   public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
        A container;
        if (isParallel()
                && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
                && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
            container = collector.supplier().get();
            BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
            forEach(u -> accumulator.accept(container, u));
        }
        else {
            container = evaluate(ReduceOps.makeRef(collector));
        }
        return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
               ? (R) container
               : collector.finisher().apply(container);
    }

构建终止操作

ReduceOps.makeRef(collector)是构建终止操作TerminalOp,针对collect方法,具体的终止操作是TerminalOp的具体实现ReduceOp,其内部封装了ReducingSink。其中ReducingSink的begin是获得Collector的Supplier作为accept的初始化,而accept是执行Collector的accumulator操作进行累加收集。

    public static <T, I> TerminalOp<T, I>
    makeRef(Collector<? super T, I, ?> collector) {
        Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
        BiConsumer<I, ? super T> accumulator = collector.accumulator();
        BinaryOperator<I> combiner = collector.combiner();
        class ReducingSink extends Box<I>
                implements AccumulatingSink<T, I, ReducingSink> {
            @Override
            public void begin(long size) {
                state = supplier.get();
            }

            @Override
            public void accept(T t) {
                accumulator.accept(state, t);
            }

            @Override
            public void combine(ReducingSink other) {
                state = combiner.apply(state, other.state);
            }
        }
        return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) {
            @Override
            public ReducingSink makeSink() {
                return new ReducingSink();
            }

            @Override
            public int getOpFlags() {
                return collector.characteristics().contains(Collector.Characteristics.UNORDERED)
                       ? StreamOpFlag.NOT_ORDERED
                       : 0;
            }
        };
    }

从后往前包装Sink操作链

collect方法里的evaluate(求值)方法进行真正的迭代操作,其调用TerminalOp的evaluateSequential方法,传入流源和ReducingSink的信息,执行wrapAndCopyInto这个这个重要方法。
ReduceOps.java

        @Override
        public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
                                           Spliterator<P_IN> spliterator) {
            return helper.wrapAndCopyInto(makeSink(), spliterator).get();
        }

如下所示,首先从后往前包装Sink,传入的是当前的ReducingSink,输出的是上游阶段的Sink,从后往前不断调用之前创建阶段时的opWrapSink方法,就可以把所有阶段的Sink包装成一个Sink链。

    final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
        copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
        return sink;
    }
       final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
        Objects.requireNonNull(sink);
        for ( AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
            sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
        }
        return (Sink<P_IN>) sink;
    }

内部迭代(处理元素)

wrapAndCopyInto调用copyInto方法
AbstractPipeline.java

    @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);
        }
    }

包装完Sink之后就可以进行内部迭代了。
wrappedSink是流水线上的第一个中间操作阶段里的Sink。
当没有短路操作时,包装Sink按照begin,accept,end的步骤执行。
begin执行后(Sink链上所有begin都执行),再通过分割迭代每个元素,每个元素执行Sink操作链上的所有accept操作,最好执行end操作(Sink链上所有end)。
短路操作的情况下稍微复杂,是在accept执行的时候多一层sink.cancellationRequested()的判断,false的情况下才执行相关sink的accept操作。

参考

java8串行流源码解析

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值