前言
开发中喜欢使用stream的模式去处理数据流,因为其结合lambda让代码更加简洁,Stream的数据处理函数名也让代码的逻辑一目了然。
抽象模型介绍
Stream
stream接口是数据流转各节点的抽象,下面是stream的部分方法
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
...
}
常用实现类,这里以ReferencePipeline为例。下面是类图。
可以看到ReferencePipeline有三个实现类,下面一一解释:
Head
第一次创建Stream时默认是Head。
StatefulOp
有状态的Stream。主要是在并发执行的时候需要考虑状态问题。比如sort操作,需要收集到完整的集合才能进行排序。
@Override
final boolean opIsStateful() {
return true;
}
StatelessOp
无状态的Stream。不需要记录数据状态,可以处理完当前数据继续处理下一条数据。
@Override
final boolean opIsStateful() {
return false;
}
Sink
sink接口是对数据流操作控制的抽象,sink的主要方法
interface Sink<T> extends Consumer<T> {
// 将集合的size向终点传递
default void begin(long size) {}
// lambda处理逻辑
void accept(T t);
// 所有数据处理结束后调用
default void end() {}
}
主要实现类TerminalSink,ChainedReference
例子
下面以一个简单的例子来说明stream内部是如何运转
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
list.stream().filter(i->i>2).forEach(System.out::println);
首先需要创建一个stream对象,就是上面提到的实现了Stream接口的Head
default Stream<E> stream() {
// spliterator()获取一个迭代器,false表示非并发处理
return StreamSupport.stream(spliterator(), false);
}
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
// 实例化一个Head对象
return new ReferencePipeline.Head<>(spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel);
}
Head对象实例化完成之后,就开始调用stream的filter实现,这里并没有立刻对数据进行任何操作。而是返回一个新的stream实现类对象。因为filter是一个无状态的操作,所以返回的是StatelessOp对象。StatelessOp重写了父类的opWrapSink方法,该方法会在数据返回一个sink实现类。可以看到Sink才是真正干活的类。那么opWrapSink会在哪调用呢,这个在后面会有详细介绍。
@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
Objects.requireNonNull(predicate);
// 将自身的引用作为参数传递到下一个stream
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) {
// 对数据进行check,将符合条件的传递给下游节点
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
继续到新的stream的foreach方法,因为foreach是一个terminal操作,所以不会再再返回stream对象,而是触发对数据的处理行为。
@Override
public void forEach(Consumer<? super P_OUT> action) {
// makeRef会返回TerminalSink的实现类OfRef
// 接着evaluate方法去执行数据的处理工作
evaluate(ForEachOps.makeRef(action, false));
}
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);
}
}
evaluate会根据当前stream是否并发处理的状态来选择不同的处理行为
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()));
}
evaluateSequential会调用最后一个stream对象的wrapAndCopyInto,具体做了什么我们继续往下看
@Override
public <S> Void evaluateSequential(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
return helper.wrapAndCopyInto(this, spliterator).get();
}
wrapSink会通过逆循环的方式找到流的第一个stream对象的sink,然后依次调用
@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;
}
走到这里我们就可以回答opWrapSink是如何使用的了,从当前节点依次向上查找自己的上一个节点,并通过调用opWrapSink方法来实例化各自的sink实现类,最后将第一个节点的sink对象返回,用于数据流的初始处理。
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;
}
这里拿到第一个节点即Head下面的Sink对象。并依次调用begin,accept,end方法。其中accept因为实在foreach里调用会被循环多次。因为我们例子里面的集合是ArrayList,所以这里的spliterator就是 ArrayListSpliterator
@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);
// 所有数据处理完成后再调用end方法
wrappedSink.end();
}
else {
copyIntoWithCancel(wrappedSink, spliterator);
}
}
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];
// 集合里的元素每个都通过accept方法向下游传递,实现数据的流化
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
刚才看的是无状态的stream,那么有状态的stream是如何处理数据的呢?下面以sort为例:
@Override
public final Stream<P_OUT> sorted() {
return SortedOps.makeRef(this);
}
创建OfRef对象,其是StatefulOp的子类
static <T> Stream<T> makeRef(AbstractPipeline<?, T, ?> upstream) {
return new OfRef<>(upstream);
}
@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);
}
核心的逻辑在这里,当accept方法接收到数据后,并不是传递给下游的sink(对比filter的sink实现),而是将接收到的数据先暂存到一个list里面,相当于数据流在这里被阻断了,知道end方法被调用的时候,才开始对list数据做排序处理,处理结束后再将数据分发给下游sink,重新启动流。
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);
}
}