目录
动机
最近想了解下stream的原理,于是做此记录。
概念
先说说stream API用来干啥的吧,设计API总有个目的吧,以我来看就是简化下代码,比如有个这样的需求:从List把所有元素*2,最后将每个元素输出,我们可能要写如下代码。
List<Integer> list = Arrays.asList(1,2,3,4,5);
for(Integer i :list){
System.out.println(i*2);
}
那stream API怎么写呢?
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream().map(t->{return t*2;}).forEach(System.out::println);
没看出有多大方便之处啊,那现在我们变下需求,在*2之后,加上不要=2的。那就有如下差异。
List<Integer> list = Arrays.asList(1,2,3,4,5);
for(Integer i :list){
if(i != 2 )
System.out.println(i*2);
}
list.stream().map(t->{return t*2;}).filter((t)->{return t!=2;}).forEach(System.out::println);
其实在我看来,stream就是简化了代码,是代码看起来简单易懂,当然stream还有并行的处理方式,这里先不做探讨。
流程
了解了大概概念后,想知道如何执行,或者说stream模型是啥样的,先看个简单图片。
其实stream模型大致如此,每个一个个stage形成链表,而每个stage封装了数据源和回调方法,这样逐个stage执行回调一起执行。下面看下代码,比如1个map().
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));
}
};
}
};
}
map()会返回个StateLessOp(AbstractPipeline)即1个stage,并且重写了opWrapSink方法,flags为操作标记,sink是接收返回结果的,即sink是ChainedReference,mapper即回调。这样我们代码一旦调用sink的accept方法,那就会先执行回调,然会调用下一个sink的accept,即每个stage执行先触发自己的回调,然后再调用下个stage的accept。这里没说sink的模型,sink其实是个单向链表。
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);
}
}
总结
模型组件,AbstractPipeline阶段、Sink操作回调封装、Spliterator加强版Iterator。