上一篇 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循环结束。
到这里,串行流的运行流程就全部分析完毕了,如果换用其他操作,效果虽然不同,但是流程大致上是相同的。
参考资料
- https://blog.youkuaiyun.com/TheLudlows/article/details/84778817
- 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
- https://www.cnblogs.com/CarpenterLee/archive/2017/03/28/6637118.html
- https://blog.youkuaiyun.com/weixin_41044036/article/details/113516439
- https://blog.youkuaiyun.com/qq_31865983/article/details/106443244
- https://www.cnblogs.com/CarpenterLee/p/6637118.html
- https://blog.youkuaiyun.com/xiliunian/article/details/88364200?spm=1001.2014.3001.5502
- https://blog.youkuaiyun.com/weixin_41131531/article/details/100007974