java8特性 stream笔记(源码解析部分)

上一篇 java8特性 stream笔记(工作流程与使用方式部分)
https://blog.youkuaiyun.com/Ding_Creator/article/details/117655158

源码解析

这里借用一下来自https://blog.youkuaiyun.com/TheLudlows/article/details/84778817的图片
在这里插入图片描述
乍一看可能会比较懵,但是别急,我们先了解一下整个stream的结构。这里再借用一下https://www.cnblogs.com/CarpenterLee/p/6637118.html的一张图。
在这里插入图片描述
通过这张图我们大致知道了stream的结构。当我们生成流的时候,是生成了一个头(Head),之后中间操作则是生成有状态(statefulOp)或是无状态(statelessOp)。这张图没有表现出终止操作。

相信大家结合两张图已经看出来了,无论是Head,还是statefulOp亦或是statelessOp,都是ReferencePipeline的子类。而ReferencePipeline又是AbstractPipeline的子类,于是我们可以看下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";
    // 源阶段
    private final AbstractPipeline sourceStage;
    // 上一阶段
    private final AbstractPipeline previousStage;
    protected final int sourceOrOpFlags;
    // 下一阶段
    private AbstractPipeline nextStage;
    // 深度
    private int depth;
    private int combinedFlags;
    // 源迭代器
    private Spliterator<?> sourceSpliterator;
    // 生成元素的方法
    // 之前的文章有提到,可以使用Stream的静态方法生成流
    // 只有这种情况下,此属性才有值
    private Supplier<? extends Spliterator<?>> sourceSupplier;
    // 流是否被连接或被消费
    private boolean linkedOrConsumed;
    private boolean sourceAnyStateful;
    private Runnable sourceCloseAction;
    // 是否并行(用于区分是串行流还是并行流)
    private boolean parallel;
    // 方法先省略,先看属性
}

看完以后我们发现,这是一个双向链表的结构,每个AbstractPipeline都有记录源阶段、上一阶段与下一阶段。接下来我们开始探究流的工作过程。我们先以无状态中间操作+非短路终止操作为例

第一步,生成流

List<Integer> list = new ArrayList<>();
list.add(3);
list.add(2);
list.add(3);
list.stream()

这里以ArrayList生成的流作为例子。那么为什么不使用Arrays.asList()这种方式呢?

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

乍一看,也是返回的一个ArrayList,但是如果你再看这个ArrayList,你会发现这个ArrayList是Arrays的一个内部类,并非我们常用的那个ArrayList,用这个方法实际上是走的数组生成流的工作流程,当然其跟ArrayList生成的流的工作流程是大同小异的。既然我们一开始就决定了使用ArrayList的例子,那么还是保持初心吧。在熟悉了ArrayList的流程后,很快就能搞清楚数组的流程。

回到正题,我们看看这个list.stream()做了什么。

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

这个spliterator()是做什么的呢?这里要注意,不要直接Ctrl+左键点进去,否则看到的是Collection中的spliterator()方法,而我们的ArrayList重写了spliterator()方法

@Override
public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

可见这个spliterator()方法返回的是一个ArrayListSpliterator,那么这个this是什么呢?显然是调用spliterator()方法的ArrayList。我们继续看下这个ArrayListSpliterator的构造器。

ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                             int expectedModCount) {
     this.list = list; // OK if null unless traversed
     this.index = origin;
     this.fence = fence;
     this.expectedModCount = expectedModCount;
}

我们的ArrayList的引用被放到了这个ArrayListSpliterator的list属性里面,也就是说集合的引用被保存到了spliterator里面。回到前面的stream()方法,这个调用spliterator()方法返回的结果被放到了StreamSupport.stream(spliterator(), false)方法的第一个参数,也就是说集合的引用被保存到了stream中。这也就证实了第一篇文章所说的:stream本身不储存元素。

我们继续看StreamSupport.stream(spliterator(), false)方法

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

Head(Spliterator<?> source, int sourceFlags, boolean parallel) {
    super(source, sourceFlags, parallel);
}

可见返回的是一个Head,继续看父类构造器

ReferencePipeline(Spliterator<?> source,
                      int sourceFlags, boolean parallel) {
    super(source, sourceFlags, parallel);
}

AbstractPipeline(Spliterator<?> source, int sourceFlags, boolean parallel) {
    this.previousStage = null;
    this.sourceSpliterator = source;
    this.sourceStage = this;
    this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
    this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
    this.depth = 0;
    this.parallel = parallel;
}

首先,入参的source就是我们刚才传入的ArrayListSpliterator,也就是说集合的引用就此被保存到了Head中。可以看下这个AbstractPipeline的构造器,首先上一阶段previousStage肯定是null,因为现在这个就是第一个。ArrayListSpliterator被保存到了sourceSpliterator属性中,源阶段则是自己,接下来两个标识符略过,深度为0,最后一个是否并行也是最开始传入的。

可能有人会好奇,一开始不是说是个双向链表吗?那么下一阶段呢?别急,在这个时候,下一阶段仍然不知道是什么,我们接下来做中间操作的时候,会引入这个值,我们继续往下看。

我们现在已经搞清楚了当我们list.stream()的时候,做了哪些操作了,接下来我们看看中间操作。这里我们使用filter为例。

list.stream().filter(new Predicate<Integer>() {
    @Override
    public boolean test(Integer integer) {
        return integer > 3;
    }
})

这里为了方便看,不使用lambda表达式。我们看看filter()方法做了什么

@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
    Objects.requireNonNull(predicate);
    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);
                }
            };
        }
    };
}

filter()返回的是一个statelessOp,也就是无状态操作。这里我们眼熟一下opWrapSink这个方法,后面很重要,但是现在暂时还用不到。我们接着往下看StatelessOp的构造器,注意这里仍然有一个this,老规矩,因为是Head调用的filter()方法(为什么呢?前面有提到list.stream()返回的是一个Head),所以这个this就是刚才的那个Head。

StatelessOp(AbstractPipeline<?, E_IN, ?> upstream, StreamShape inputShape, int opFlags) {
   super(upstream, opFlags);
   assert upstream.getOutputShape() == inputShape;
}

ReferencePipeline(AbstractPipeline<?, P_IN, ?> upstream, int opFlags) {
    super(upstream, opFlags);
}

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

这里就有意思了,这里会检查linkedOrConsumed属性是否为true,如果为true会报错,否则则把这个属性设置为true。这也就解释了第一篇文章提到的流是一次性的。

接下来,这个previousStage就是我们刚才解释的Head,那么这个previousStage.nextStage = this实际上就是Head的nextStage属性为this(注意,这里的this已经变成了statelessOp)也就是说,Head的下一阶段为statelessOp,同时,statelessOp的上一阶段previousStage 为Head,也就是说双向链表的结构就这样完成了(可喜可贺?)

此外,源阶段属性也从上一阶段获取后放入sourceStage属性,深度+1(这个深度后面也有用的)

至此,中间操作也完成了。我们可以发现,到中间操作也只是构建双向链表,并没有对流中元素做任何操作,这就是为什么说没有终止操作,整个流不会执行。

重头戏来了,我们看看终止操作做了什么(这一部分会比较复杂)
这里我们使用forEach方法为例,同样的,为了方便看,不使用lambda表达式

list.stream().filter(new Predicate<Integer>() {
    @Override
    public boolean test(Integer integer) {
        return integer > 3;
    }
}).forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) {
         System.out.println(integer);
    }
});

我们看看forEach方法做了什么,这里会有两个forEach,其中一个是Head中重写的forEach,Head直接调用的流程上会有些许不一样,但是大致流程是一样的。我们既然看的是有中间操作的,那么现在应该看ReferencePipeline中的forEach方法。

@Override
public void forEach(Consumer<? super P_OUT> action) {
    evaluate(ForEachOps.makeRef(action, false));
}

我们先看看ForEachOps.makeRef(action, false)这个返回的是个什么(action就是我们重写的那个方法)

public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action, boolean ordered) {
    Objects.requireNonNull(action);
    return new ForEachOp.OfRef<>(action, ordered);
}

这个ordered传入的是个false,什么意思呢?其实在串行流中,这个字段是没有作用的,但是在并行流中,由于各任务完成速度不一致,最后很有可能是乱序输出的,ordered为false表明是无序,如果要保证有序,则可以使用forEachOrdered,这个方法的ordered字段为true。

这里返回的是一个TerminalOp,顾名思义,是一个终止操作。TerminalOp是所有终止操作的父类,除了ForEachOp外,还有reduceOp,matchOp,FindOp等子类,看命名,是不是很眼熟?它们跟各个终止操作的命名非常相近。

继续往下看ForEachOp.OfRef

static final class OfRef<T> extends ForEachOp<T> {
    final Consumer<? super T> consumer;

    OfRef(Consumer<? super T> consumer, boolean ordered) {
            super(ordered);
            this.consumer = consumer;
    }

    @Override
    public void accept(T t) {
        consumer.accept(t);
    }
}

我们重写的forEach中的内容被保存到了consumer这一属性中,且当这个类调用accept方法时,会调用我们重写的accept方法(前面我们没有用lambda表达式,可以看出来我们就是重写了这个accept方法)

我们倒回到evaluate这个方法

@Override
public void forEach(Consumer<? super P_OUT> action) {
    evaluate(ForEachOps.makeRef(action, false));
}

final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
    assert getOutputShape() == terminalOp.inputShape();
    if (linkedOrConsumed)
        throw new IllegalStateException(MSG_STREAM_LINKED);
    linkedOrConsumed = true;

    return isParallel()
        ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
        : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
}

这里也有一个判断linkedOrConsumed属性的条件,我们第一篇文章多个终止操作会报错也正是源于此。
接下来,串行流和并行流会走不同的路线。因为并行流笔者还没完全搞明白,所以这里只看串行流这条路线,sourceSpliterator(terminalOp.getOpFlags())返回源阶段保存的迭代器

private Spliterator<?> sourceSpliterator(int terminalFlags) {
    // Get the source spliterator of the pipeline
    Spliterator<?> spliterator = null;
    if (sourceStage.sourceSpliterator != null) {
        spliterator = sourceStage.sourceSpliterator;
        sourceStage.sourceSpliterator = null;
    }
    else if (sourceStage.sourceSupplier != null) {
        spliterator = (Spliterator<?>) sourceStage.sourceSupplier.get();
        sourceStage.sourceSupplier = null;
    }
    else {
        throw new IllegalStateException(MSG_CONSUMED);
    }
    // 中间这段只有并行流才能用到的代码省略
    if (terminalFlags != 0)  {
        // Apply flags from the terminal operation to last pipeline stage
        combinedFlags = StreamOpFlag.combineOpFlags(terminalFlags, combinedFlags);
    }
    
    return spliterator;
}

我们忽略标识符部分的变化(这点必须夸一下官方,虽然看不懂标识符的变化,但是判断条件能很清晰看懂),我们看下这里做了什么。首先会判断源阶段是否有迭代器(我们用集合生成的流是有的),如果没有,则看有没有supplier(如果用Stream的静态方法生成的流是有的)。回到我们的例子本身,返回的是一个迭代器,前面有提到,是一个ArrayListSpliterator。

那么我们回到这里

terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));

老问题了,这个this是什么呢?是调用了forEach方法的上一阶段(在我们的例子中是一个statelessOp),第二个参数则是一个ArrayListSpliterator。

继续往下,会发现不同的terminalOp有不同的实现,这里我们看ForEachOp(别忘了我们这个例子中是OfRef哦)下是怎么实现的

@Override
public <S> Void evaluateSequential(PipelineHelper<T> helper,
                                           Spliterator<S> spliterator) {
    return helper.wrapAndCopyInto(this, spliterator).get();
}

这里的this又变成了刚才那个OfRef,因为这个方法是terminalOp调用的。我们继续看这个方法做了什么

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

有意思的东西来了,这个入参的sink是什么?原来OfRef的父类ForEachOp还实现了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) {
        sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
    }
    return (Sink<P_IN>) sink;
}

这出现了一个for循环,那么这个this又是什么呢?用之前的方法可以判断出这是终止操作的前一个阶段。那么opWrapSink又是干什么的呢?仔细看看这个opWrapSink,有没有觉得眼熟?还记得我们之前在中间操作filter部分提到的需要眼熟一下的东西吗?

public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
    Objects.requireNonNull(predicate);
    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);
                }
            };
        }
    };
}

没错了,中间操作是有重写opWrapSink方法的。这里注意,我们把终止操作作为参数传入了ChainedReference。我们看一下ChainedReference的结构

static abstract class ChainedReference<T, E_OUT> implements Sink<T> {
   protected final Sink<? super E_OUT> downstream;

   public ChainedReference(Sink<? super E_OUT> downstream) {
        this.downstream = Objects.requireNonNull(downstream);
   }

   @Override
   public void begin(long size) {
        downstream.begin(size);
   }

   @Override
   public void end() {
        downstream.end();
   }

   @Override
   public boolean cancellationRequested() {
        return downstream.cancellationRequested();
    }
}

终止操作被放入了downStream属性中,也就是说opWrapSink实际上返回了一个带有终止操作(下一阶段)的sink

回到刚才的for循环

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

我们发现,上一次返回的sink会作为下一次的入参。刚才有提到是从终止操作的前一阶段开始循环的,每次循环往前走一个阶段,那么什么时候结束呢?注意depth深度这个属性,当这个属性大于0会一直往前,一直到depth=0为止,我们前面有提到,Head的depth=0,而Head因为depth=0并不会执行for循环中的操作,所以是一直到Head的后一阶段结束。

刚才也提到,作为入参的sink会成为downStream属性,而返回的sink又会作为下一次的入参,那么sink的结构就很明了了
在这里插入图片描述
其实就是每个上一阶段的sink里面都包含了下一阶段的sink
回到copyInto这里

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

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

虽然我们刚才并没有仔细计算标识符,但是看判断条件就知道非短路操作会走if里面这个流程,而短路操作则会走copyIntoWithCancel这个流程。我们现在用做例子的是非短路操作forEach,所以我们看if里面这个流程。

首先是调用sink的begin方法,我们倒回前面sink的结构的部分,可以看到filter重写的begin方法会调用下一阶段sink的begin方法,终止操作部分的OfRef和ForEachOp都没有重写这个方法,所以其使用的是Sink中的默认方法,该方法没有做任何操作。

接下来会调用spliterator(其实就是ArrayListSpliterator,不知道怎么来的可以再仔细看看前面的流程)的forEachRemaining方法。

public void forEachRemaining(Consumer<? super E> action) {
    int i, hi, mc; // hoist accesses and checks from loop
    ArrayList<E> lst; Object[] a;
    if (action == null)
        throw new NullPointerException();
    if ((lst = list) != null && (a = lst.elementData) != null) {
        if ((hi = fence) < 0) {
            mc = lst.modCount;
            hi = lst.size;
        }
        else
            mc = expectedModCount;
        if ((i = index) >= 0 && (index = hi) <= a.length) {
            for (; i < hi; ++i) {
                @SuppressWarnings("unchecked") E e = (E) a[i];
                action.accept(e);
            }
            if (lst.modCount == mc)
                return;
        }
    }
    throw new ConcurrentModificationException();
}

详细的流程就不细讲了,只要知道这里其实是使用了一个for循环将集合中的元素逐个放入sink的accept方法的入参。

这样我们在filter中重写Predicate的test方法就起作用了,根据之前filter的源码,我们可以知道,只有当元素进入我们重写的方法后返回true才能继续调用下一阶段sink的accept方法。一直到终止操作的accept方法中,终止操作的accept方法会调用我们在终止操作中重写的方法。

最后,会调用sink的end方法,我们在filter中并没有重写end方法,但是我们之前ChainedReference的结构里面是有一个end方法的,它所做的就是调用下一阶段sink的end方法。直到终止操作,跟begin方法一样,OfRef和ForEachOp中没有重写end方法,而Sink中的默认方法,该方法也没有做任何操作。

总结下来就是begin和end虽然也经历了从第一个阶段到最后一个阶段的调用,但是没有做任何操作。主要起作用的是元素被逐个放入accept方法的入参中,然后每一个元素都经历从第一个阶段到最后一个阶段。
在这里插入图片描述
那么这里有一个疑问了,明明begin和end没有做任何操作,为什么还需要这两个方法呢?答案在有状态操作中。接下来我们再来看看有状态操作+短路操作的流程。因为上面已经把具体流程详细过了一遍,接下来这段就只讲不同的部分,相同的部分就直接跳过了。

class SourceStream2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(3);
        // 有状态+短路
        list.stream().sorted().anyMatch(new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                System.out.println("integer=" + integer);
                return integer > 3;
            }
        });
    }
}

生成流的部分是一样的,直接跳过。来到sorted这个方法。

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

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

// 注意这个OfRef跟之前讲无状态操作+短路操作里面出现的那个不是同一个

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

   /**
    * Sort using the provided comparator.
    *
    * @param comparator The comparator to be used to evaluate ordering.
    */
   OfRef(AbstractPipeline<?, T, ?> upstream, Comparator<? super T> comparator) {
        super(upstream, StreamShape.REFERENCE,
                  StreamOpFlag.IS_ORDERED | StreamOpFlag.NOT_SORTED);
        this.isNaturalSort = false;
        this.comparator = Objects.requireNonNull(comparator);
   }

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

   @Override
   public <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
                                                 Spliterator<P_IN> spliterator,
                                                 IntFunction<T[]> generator) {
       // If the input is already naturally sorted and this operation
       // naturally sorts then collect the output
       if (StreamOpFlag.SORTED.isKnown(helper.getStreamAndOpFlags()) && isNaturalSort) {
            return helper.evaluate(spliterator, false, generator);
       }
       else {
            // @@@ Weak two-pass parallel implementation; parallel collect, parallel sort
            T[] flattenedData = helper.evaluate(spliterator, true, generator).asArray(generator);
            Arrays.parallelSort(flattenedData, comparator);
            return Nodes.node(flattenedData);
        }
    }
}

老规矩,先看返回值类型,返回的是一个statefulOp(有状态操作),找到目标了,我们来看看opWrapSink方法跟之前有什么不一样。这里出现了三个分支,第一个分支很好理解,当流中元素已被排序,直接返回。但是第二个分支和第三个分支有什么区别呢?

分别进入第二个分支和第三个分支,发现第二个分支是用于处理数组的(也就是说size是固定的),而第三个分支才是我们应该走的。

进入RefSortingSink,一看,哇!跟之前的完全不一样

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

在这个sink中,begin方法变成了新建一个中间结果集,accept方法变成了将入参放入结果集,end方法变成了依次调用下一阶段sink的begin,accept,end方法。这就是有状态操作和无状态操作的区别了,我们之前提到,有状态操作需要等前一步骤结束才能进行下一步,结合我们刚才分析的详细实现过程可以得知,这里的sink的结构应该如下图所示
在这里插入图片描述
其中上一阶段的end依次调用下一阶段的begin、accept、end方法。(当然上一阶段的sink中包含下一阶段sink这个结构没有发生改变)

分析完有状态操作,我们最后分析一下短路操作。短路操作的终止操作和之前非短路的还是比较相似的。这里我们把matchOp的代码贴出来做个比较

public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,
        MatchKind matchKind) {
    Objects.requireNonNull(predicate);
    Objects.requireNonNull(matchKind);
    class MatchSink extends BooleanTerminalSink<T> {
        MatchSink() {
            super(matchKind);
        }

        @Override
        public void accept(T t) {
            if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
                stop = true;
                value = matchKind.shortCircuitResult;
            }
        }
    }

    return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);
}

private static final class MatchOp<T> implements TerminalOp<T, Boolean> {
    private final StreamShape inputShape;
    final MatchKind matchKind;
    final Supplier<BooleanTerminalSink<T>> sinkSupplier;

    /**
     * Constructs a {@code MatchOp}.
     *
     * @param shape the output shape of the stream pipeline
     * @param matchKind the kind of quantified match (all, any, none)
     * @param sinkSupplier {@code Supplier} for a {@code Sink} of the
     *        appropriate shape which implements the matching operation
     */
    MatchOp(StreamShape shape,
            MatchKind matchKind,
            Supplier<BooleanTerminalSink<T>> sinkSupplier) {
        this.inputShape = shape;
        this.matchKind = matchKind;
        this.sinkSupplier = sinkSupplier;
    }

    @Override
    public int getOpFlags() {
        return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED;
    }

    @Override
    public StreamShape inputShape() {
        return inputShape;
    }

    @Override
    public <S> Boolean evaluateSequential(PipelineHelper<T> helper,
                                          Spliterator<S> spliterator) {
        return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState();
    }

    @Override
    public <S> Boolean evaluateParallel(PipelineHelper<T> helper,
                                        Spliterator<S> spliterator) {
        // Approach for parallel implementation:
        // - Decompose as per usual
        // - run match on leaf chunks, call result "b"
        // - if b == matchKind.shortCircuitOn, complete early and return b
        // - else if we complete normally, return !shortCircuitOn

        return new MatchTask<>(this, helper, spliterator).invoke();
    }
}

看起来好像很复杂。我们可以发现,这里多了一个sinkSupplier属性,sinkSupplier里面是一个MatchSink对象,而MatchSink对象的accept方法逻辑是if中的条件为true时,也就是当stop这个属性为false,且Predicate中test方法(也就是我们重写的判断流程)和标志位相等(这里是anyMatch,所以标志位为true,也就是说只要我们test方法返回true,整个判断条件都为true),就会将stop属性置为true(下次就不会再进来了),同时把value属性赋值(这里为true,这个值其实是anyMatch返回的结果)。

之后的流程就与之前相同了。短路操作和非短路操作的区别在这段代码。如果不知道这段代码在哪,可以按之前分析的顺序慢慢找到这里

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

我们现在因为是短路操作,所以调用的是copyIntoWithCancel方法,看看这个方法的具体实现

@Override
@SuppressWarnings("unchecked")
final <P_IN> void copyIntoWithCancel(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
    @SuppressWarnings({"rawtypes","unchecked"})
    AbstractPipeline p = AbstractPipeline.this;
    while (p.depth > 0) {
        p = p.previousStage;
    }
    wrappedSink.begin(spliterator.getExactSizeIfKnown());
    p.forEachWithCancel(spliterator, wrappedSink);
    wrappedSink.end();
}

begin和end还是很熟悉的,那么这个forEachWithCancel又有什么玄机呢

@Override
final void forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
    do { } while (!sink.cancellationRequested() && spliterator.tryAdvance(sink));
}

原来是通过do while不断循环,cancellationRequested返回的是刚才stop的值,再看看tryAdvance方法

public boolean tryAdvance(Consumer<? super E> action) {
    if (action == null)
        throw new NullPointerException();
    int hi = getFence(), i = index;
    if (i < hi) {
        index = i + 1;
        @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
        action.accept(e);
        if (list.modCount != expectedModCount)
            throw new ConcurrentModificationException();
        return true;
    }
    return false;
}

其实就是把元素放入到accept的入参当中,每次循环时都会让index值+1让下次循环的时候能拿到下个元素,当所有元素都遍历完后返回一个false,do-while循环结束。

到这里,串行流的运行流程就全部分析完毕了,如果换用其他操作,效果虽然不同,但是流程大致上是相同的。

参考资料

  1. https://blog.youkuaiyun.com/TheLudlows/article/details/84778817
  2. https://blog.youkuaiyun.com/y4x5M0nivSrJaY3X92c/article/details/83155483?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-17.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-17.control
  3. https://www.cnblogs.com/CarpenterLee/archive/2017/03/28/6637118.html
  4. https://blog.youkuaiyun.com/weixin_41044036/article/details/113516439
  5. https://blog.youkuaiyun.com/qq_31865983/article/details/106443244
  6. https://www.cnblogs.com/CarpenterLee/p/6637118.html
  7. https://blog.youkuaiyun.com/xiliunian/article/details/88364200?spm=1001.2014.3001.5502
  8. https://blog.youkuaiyun.com/weixin_41131531/article/details/100007974
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值