序
一直想写一个Stream流源码解析的专栏,但是奈何现在水平还不行,很难讲到面面具到。而且在最近看到了一个Java8 Stream流的源码解析专栏,讲得也是非常的好,整体的结构架子也是非常清晰。
二话不说,先给上专栏链接:juejin.cn/column/7099…
本人也向作者询问了大体文章思路,深受启发。但深知自己对Stream流的整体框架还把握的不够到位且很难用从全局表述清楚,故暂时放弃此专栏的文章整理。
但是这并不妨碍我对于Stream流局部核心源码的解析。故有了此篇文章。
文章分析哪部分的源码
如标题所述,本文主要讲解stream流sink的包装解析以及元素流转源码。
文章会用一个最简单的例子以及源码跟踪的方式来讲述这部分源码。文章对于没读过Stream流源码的朋友可能依然有些不友好,但这已经是我觉得最简单的解析方式。
源码分析
总述
想了好久,我觉得还是使用类似于prep的方法来写这篇文章,效果会好上不少。
结论:1.管道的装配是从前到后的。2.sink的包装是从后到前的。3.元素的处理自然是从前到后的。
例子
list.stream()方法
Head是ReferencePipeline的内部类,最后的构造方法是
.map()方法
我们关注于返回值StatelessOp,这个无状态操作也是ReferencePipeline的内部类。(至于什么是无状态,什么是有状态,什么是头部,什么是终止操作等,本文都不予讨论,可以阅读上方专栏文章。)最后的构造方法是
ReferencePipeline词如其意,就像是个管道,有前一节previousStage,后一节nextStage,以及头部sourceStage和深度depth。这样的话可以简单的认为map方法的管道连接成功了!
.filter()
filter方法和map方法基本一模一样只是重写了返回内部类对应的方法而已。 其次依然是上方同map一样的构造方法这样就连接成功了filter管道。
在此便可以说明管道的连接是从前到后的。
注:本文不采用比较复杂的流程操作,因为那些东西不是本文讨论的重点。
.collect()
包装sink方法在最后的终止操作之中,所以不得不对.collect()方法做简单的分析。
上图中的makeRef和evaluate这两个方法都非常重要,都需要好好解析
makeRef()
makeRef返回的终止操作,其实也可以抽象的理解为管道的最后一节。
public static <T, I> TerminalOp<T, I>
makeRef(Collector<? super T, I, ?> collector) {
//我们传进来的collector主要就是supplier、BiConsumer、BinaryOperator(并行时使用)
//就比如Collectors.toList(),简单来说就是supplier提供初始化list,BiConsumer类似于add方法
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);
}
}
//这个终止操作的返回值为ReduceOp,重写了makeSink()方法,在包装sink的时候会提供最初的sink
//返回值很有意思是ReducingSink也就是上方的局部内部类,这个局部内部类可以操作supplier、
//BiConsumer、BinaryOperator ,这样也就提供了最后一节管道的操作。
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;
}
};
}
evaluate()
这边的makeSink()其实也可以简单的理解为最后一节管道对象。
核心方法wrapAndCopyInto()
首先分析包装sink方法
@Override
@SuppressWarnings("unchecked")
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink);
//这个for循环非常有意思!
//首先必须清楚AbstractPipeline.this是最后第二节管道也就是filter管道。为什么?因为evaluate方法的调
//用者为AbstractPipeline,而管道对象为ReferencePipeline继承自AbstractPipeline。所以最后是装配到
//filter管道,所以AbstractPipeline.this是filter管道。
//filter管道深度是2所以进入循环包装filter管道的sink,在此处也见过了非常熟悉的opWrapSink方法
//map和filter的返回值都重写了这个方法,下面会仔细分析这个方法
//第一次遍历结束后p变成了上一节管道也就是map管道,深度为1继续包装map管道的sink
//包装完后上一节管道为head管道即第一节管道,深度为0至此循环结束,包装sink结束,返回sink
//在此便可以说明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;
}
opWrapSink方法需要仔细分析
首先看filter管道的opWrapSink方法
首先附上makeRef中的局部内部类,也就是最后一节管道,用于下面代码跟踪
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);
}
}
@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) {
//opWrapSink的返回值为Sink接口的内部类ChainedReference
//构造方法为传入的sink即为下游处理器
//可以重写begin,end,accept等方法用于不同的元素处理
//现在列子中传入sink即为终止操作的对象也可理解为最后一节管道
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
//此出列子中掉用即为makeRef中那个局部内部类的begin方法
//创建一个list对象
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
if (predicate.test(u))
//此出列子中掉用即为makeRef中那个局部内部类的accept方法
//往list中加入元素
downstream.accept(u);
}
};
}
};
}
接着看map管道的opWrapSink方法
@Override
@SuppressWarnings("unchecked")
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) {
//此处的sink对象即为包装好的filter管道以及最后一节管道
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void accept(P_OUT u) {
//此处accept方法就会去调用filter管道的accept方法
//也就是去进行数据的过滤
//至此与元素流转其实也已经明了
downstream.accept(mapper.apply(u));
}
};
}
};
}
最后分析copyInto方法即为元素流转方法
定位到ArrayListSpliterator类,是ArrayList的内部类。
public void forEachRemaining(Consumer<? super E> action) {
//action其实就是包装好的sink对象也可以说是ChainedReference对象
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];
//掉用sink的第一层也就是map管道
//然后map那边的downStream会去掉filter的accept
//最后filter的downStream会去掉最后一节管道的的accept即把数据装入list
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
尾
至此,Stream流sink的包装解析以及元素流转大致也就很清楚了。 能看到此处的也不容易,非常感谢!
如文中有错误或不妥之处请不吝指教,再次感谢!