java8串行流源码解析

中间操作和流源都属于ReferencePipeline(即Stream),终止操作属于TerminalOp。

中间操作的Sink属于ChainedReference,终止操作的Sink属于TerminalSink。

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操作完成内部迭代了。

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

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

构建流源比重很大的一个参数是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,它是无状态中间操作阶段的基类。StatelessOp的构建其实就是构建双向链表的节点。

方法中还实现了opWrapSink方法,构建了一个ChainedReference,该类用于创建Sink链节点,它接收一个未知输入类型的下游Sink,是Sink的一个抽象实现。Sink是Comsumer接口的一个扩展,用于传导流管道上的元素。在第一次调用Sink的accept方法前,必须调用begin方法通知有多少数据已经到来,所有数据处理完毕,必须调用end方法,cancellationRequested方法用于取消接受数据。包装Sink的实质就是如何重载Sink的四个接口方法,实现数据的不断消费并发送。

map负责链接上游的阶段,形成双向链表的数据结构,内部封装一个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(sorted前是乱序且数据大小非固定):

@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.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:

@Override
@SuppressWarnings("unchecked")
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其中begin是获得Collector的Supplier作为accept的初始化,而accept是执行accumulator操作进行累加收集。

evaluate方法进行真正的迭代操作,其调用TerminalOp的evaluateSequential方法,传入流源和ReducingSink的信息,执行wrapAndCopyIndo这个重要的方法,其从后往前包装Sink,传入的是当前的ReducingSink,输出的是上游阶段的Sink,从后往前不断调用该方法,就可以把所有阶段的Sink包装成一个Sink链。

包装完Sink之后就可以进行内部迭代了,当没有短路操作是,包装Sink按照begin,accept,end的步骤执行,begin执行后,再通过分割迭代器迭代每个元素,每个元素执行各自的accept操作,最后执行end操作。短路操作的情况稍微复杂,是在accept执行的时候多一层sink.cancellationRequested()的判断,false的情况下才执行sink的accept操作。

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值