书接上文,上一篇中对 Java 8 的行为接口做了简单的介绍,本篇将对 Java 8 集合重要操作 流式处理类 Stream 做简单的介绍,有了前面的铺垫,阅读 Stream 的源码就比较清晰了。
首先来了解一下 Stream 大致概念。
Stream 的核心概念
管道<–>阶段,中间操作<–>终结操作,状态无关操作<–>状态相关操作,短路操作<–>非短路操作,这四组概念是 Stream 流式处理最核心的概念,在阅读流式处理的相关源码之前,必须有一个大体的认知:
- 管道<–>阶段
Pipeline,流式处理提供了一种将对数据的操作且分成多个阶段,根据每个阶段的性质来对数据进行操作的方式。举一个例子,如果有一个业务逻辑,需要从 Integer 列表中筛选出大于零的元素,将它们乘以二,再构造成一个新的 Integer 列表返回,那么在 Java 8 流式处理出现之前,实现的方式大概如下:
List<Integer> list = Arrays.asList(-1, 2, 3);
/**Java 8 流式操作出现之前**/
List<Integer> result = new ArrayList<>();
for (Integer i : list) {
if (i > 0) {
i *= 2;
result.add(i);
}
}
而通过 Java 8 的流式处理操作,实现方式就变成了
List<Integer> list = Arrays.asList(-1, 2, 3);
/**Java 8 流失操作实现相同逻辑**/
List<Integer> result = list.stream()
.filter(e -> e > 0).map(e -> e * 2)
.collect(Collectors.toList());
之前需要 7 行代码实现的逻辑现在只需要一行就可以了,而且可读性明显强了很多。现在基于这段流式操作来更加直观的说明管道与阶段。以上操作的管道与阶段可以通过下图来表示:
这一整条链路就是一根完整的管道,每一个四边形对应一个阶段,每一个箭头则对应一个流式操作方法。
- 中间操作<–>终结操作
Stream 接口中定义了许多流式操作方法,有些是中间操作,对应上图中的 IntermidateOp 阶段,有些事终结操作,对应上图中的 CircuitingOp 阶段,有一种很简单的分辨方法就是流式操作要求参数与返回都是流相关类,所以 Stream 中返回类型为 Stream 对象的方法都属于中间操作,剩下的都是终结操作。 - 状态无关操作<–>状态相关操作
状态无关与状态相关是对于 中间操作 的一种描述,流式操作的某些中间操作方法提供了并行元算的支持,而判断能否并行运算的依据就是这个阶段操作的状态相关性,某些情况下后一个阶段必须等待前一个阶段处理完成后才能继续,比如 distinct 操作,其他一些情况前后阶段可以同时进行,并不影响操作结果,比如 map 操作。Stream 接口的 Javadoc 中有描述一个方法是否是状态无关操作。 - 短路操作<–>非短路操作
短路与非段路是对于 终结操作 的一种描述,流失操作的某些终结方法是可以在条件触发后立刻返回结果的,比如 findFirst 操作,这些操作就是短路操作,还有一些必须对所有元素进行运算后才能获得结果,比如 max 操作。Stream 接口的 Javadoc 中有描述一个方法是否是短路操作。
对于中间操作<–>终结操作,状态无关操作<–>状态相关操作,短路操作<–>非短路操作可以归纳为下表:
中间操作(IntermidateOp) | 状态无关操作(StatelessOp) | filter, map, mapToInt, mapToLong, mapToDouble, flatMap, flatMapToInt, faltMapToLong, flatMapToDouble |
状态相关操作(StatefulOp) | distinct, sorted, limit, skip | |
特殊方法 | 官方建议测试操作 | peek |
终结操作(IntermidateOp) | 短路操作(Short-circuiting Op) | anyMatch, findFirst, findAny |
非短路操作(No-Short-circuiting Op) | forEach, forEachOrdered, toArray, reduce, collect, min, max, count, allMatch, noneMatch |
Stream 接口的继承关系
先来看一下 Stream 的继承关系
从图中可以看出:
- 流式处理类具有关闭能力(继承自 AutoCloseable 接口,该接口要求实现类实现 close() 方法)
- 从图中第二层到第三层,有四个接口继承了 BaseStream,除了 Stream 接口外还有 DoubleStream、IntStream 与 LongStream,后三种是 Java 8 对与 double、int、long 类型的定制优化,了解 Stream 接口以后后面三种也是类似的
- 从图中第三层到第四层,流式处理实现抽象了除了实现了流式接口,还继承了 AbstractPipleline 抽象类,AbstractPipeline 有继承了 PipelineHelper 类,这里使用了接口隔离原则和合成复用原则,类似于 Collection 与 Iterator 的关系,将流式与管道拆开设计,并在流式处理抽象类中复合使用,可以确定 AbstractPipeline 提供的方法是相同的,只是在不同的 Pipleline 子类中实现不同而已,这种设计隐藏了不同流的内部结构
- 从图中第四层到第五层,每一种 Pipleline 实现类,都有 StatelessOp 与 StatefulOp 内部类,即状态无关操作与状态相关操作。
所以要了解一条完整 Stream 的链路,至少需要了解 AutoCloseable -> BaseStream -> PipelineHelper -> AbstractPipeline -> Stream -> ReferencePipeline -> StatelessOp -> StatefulOp 这些接口与类,下面就对它们进行逐一分析。
AutoCloseable
关闭能力接口
public interface AutoCloseable {
/**抽象关闭方法,使实现类具备关闭能力**/
void close() throws Exception;
}
BaseStream
/**BaseStream 接口,继承自 AutoCloseable,拥有关闭能力,要求第二个范型参数继承自 BaseStream 类**/
public interface BaseStream<T, S extends BaseStream<T, S>>
extends AutoCloseable {
/**返回流实现类的迭代器**/
Iterator<T> iterator();
/**返回流实现类的并行迭代器**/
Spliterator<T> spliterator();
/**判断是否是并行化流**/
boolean isParallel();
/**将流串行化**/
S sequential();
/**将流并行化**/
S parallel();
/**解除有序流的顺序限制,发挥并行处理的优势**/
S unordered();
/**返回一个新的 Stream,新 Stream 的元素依然是 T,当 close 方法调用时,closeHandler 才会执行**/
S onClose(Runnable closeHandler);
/**继承自 AutoCloseable 的方法,需要实现类实现**/
@Override
void close();
}
Iterator iterator()
返回一个串行迭代器,Spliterator spliterator()
返回一个并行迭代器,这两个方法还出现在了 Iterable 接口中,Iterable 接口将在能力接口篇中进行分析,这里只需要知道其实所有实现了 Iterable 接口的组数据结构类与流式处理类一样,从 Java 8 开始都具备了并行迭代的能力。Iterator 接口与 Spliterator 接口将在能力器接口篇中进行分析。其它方法在 Stream 的业界最佳实现抽象类中都会实现,所以这里只需了解这些方法的用处即可。
这里有一个特别的方法,S onClose(Runnable closeHandler)
是一个关闭钩子方法,在流关闭的时候才会执行一段线程操作。
PipelineHelper
/**流式线助理类--管道助理**/
abstract class PipelineHelper<P_OUT> {
/**返回一个 StreamShape 枚举类对象**/
abstract StreamShape getSourceShape();
/**获取一个流和操作标志的值,流操作标志在 StreamOpFlag 枚举类中定义**/
abstract int getStreamAndOpFlags();
/**接收一个并行迭代器参数,并行迭代器中有一个方法会 getExactSizeIfKnown 会返回对 这个并行迭代器中元素个数的大致或准确的数值,这个方法将在能力器接口篇中分析,如果这个数值确定了,返回该值,否则返回 -1**/
abstract<P_IN> long exactOutputSizeIfKnown(Spliterator<P_IN> spliterator);
/**接收一个 sink 对象与一个并行迭代器对象,将在这个 pageHepler 中描述的各管道阶段应用于迭代器上,并将结果发送至 sink,然后将其返回,Sink 的介绍可以参考行为接口篇,这里可以简单的理解一个带有容量描述,可以重置的消费行为对象**/
abstract<P_IN, S extends Sink<P_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator);
/**接收一个 sink 对象与一个并行迭代器对象,将并行迭代器中的对象推送至 sink 中,如果 pipelineHepler 中有短路阶段(首次出现短路阶段,将在下面 Stream 的介绍中进行解释),那么每个元素操作完后都会调用 Sink#cancellationRequested() 进行检查,如果为 true 则会停止操作**/
abstract<P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);
/**与上一个方法相同,只是这个方法中不需要判断 pipelineHelper 中是否有短路阶段,而是直接在每个元素操作完后调用 Sink#cancellationRequested() 进行检查,为 true 则终止**/
abstract <P_IN> void copyIntoWithCancel(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);
/**接收一个范型类型为 P_OUT 的 sink,将其包装成一个范型类行为 P_IN 的 sink,并对其应用这个 pipelineHepler 中定义的所有中间操作(首次出现中间操作,将在下面 Stream 中的介绍中进行解释),将结果传递给返回的 sink 对象**/
abstract<P_IN> Sink<P_IN> wrapSink(Sink<P_OUT> sink);
/**接收一个范型为 P_IN 的并行迭代器参,返回一个反省为 P_OUT 的并行迭代器,在远嘛中没有对这个方法的描述,可以简单理解为开放实现**/
abstract<P_IN> Spliterator<P_OUT> wrapSpliterator(Spliterator<P_IN> spliterator);
/**传入一个 long 的元素数量已知参数与一个 IntFunction 对象,返回一个可变的 Node 构造器,Node 是 Java 8 中新增的一种数据结构,将在数据结构篇中做详解,这里可以先简单的理解为一种树形数据结构,它本身是一个不可变类,而 Node.Builder 是让其具有可变能力的构造器,类似 String 与 StringBuilder 的关系,不同的是对 StringBuilder 的操作返回的仍是 StringBuilder,而对 Node.Builder 的操作将直接返回 Node**/
abstract Node.Builder<P_OUT> makeNodeBuilder(long exactSizeIfKnown,
IntFunction<P_OUT[]> generator);
/**传入一个并行迭代器参数,boolean 参数(用于判断这个 pipeline 是否可并行化,如果可以并行化则会返回一个叶子节点,否则将返回一颗树),一个 IntFunction 参数,将 pipeline 的所有阶段应用到并行迭代器上,并收集所有的结果,构建一个 Node 返回对象**/
abstract<P_IN> Node<P_OUT> evaluate(Spliterator<P_IN> spliterator,
boolean flatten,
IntFunction<P_OUT[]> generator);
}
由此可知,PiplelineHelper 主要是一个根据 Stream 中不同阶段,来消费并行迭代器的助理类。
AbstractPipeline
AbstractPipeline 是一种链式数据结构,本来不应该放在这篇来分析,但是它并不属于 Java 经典数据结构之一,并且对 Stream 的分析需要用到,所以就将它放在此处,AbstractPipleline 的描述非常多,这里只选取一些我认为重要的,并没有放上它的所有源码
/**管道类的基础抽象类,提供了 Stream 接口的核心实现和一些主要准则**/
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";
/**管道的源阶段,管道链头的反链(不知道该怎么翻译 backlink。。),如果本身是源阶段的话,就是它自己**/
@SuppressWarnings("rawtypes")
private final AbstractPipeline sourceStage;
/**管道上游管道,如果管道本身是源阶段的话,则为 null**/
@SuppressWarnings("rawtypes")
private final AbstractPipeline previousStage;
/**管道下游管道,如果管道本身是最后阶段的话,则为 null,高效的链接至下一个管道**/
@SuppressWarnings("rawtypes")
private AbstractPipeline nextStage;
/**
注意以上的三种 Pipeline 对象的定义与关系,说明了 AbstractPipleline 是一种链式数据结构
**/
/**源阶段范型对应的并行迭代器,只针对头部管道**/
private Spliterator<?> sourceSpliterator;
/**源阶段范型的提供行为对象,提供行为接口请参看行为接口篇,只针对头部管道**/
private Supplier<? extends Spliterator<?>> sourceSupplier;
/**源阶段关闭勾子线程(对应 BaseStream 类的 S onClose(Runnable closeHandler) 方法参数),只针对头部管道**/
private Runnable sourceCloseAction;
/**如果管道是可并行化的则为 true,否则为 false,只针对头部管道**/
private boolean parallel;
/**头部管道构造器,接收源提供行为参数,源或操作标志参数,可并行化标志参数,在 AbstractPipeline 源码中还定义了其他头部管道构造器,**/
AbstractPipeline(Supplier<? extends Spliterator<?>> source,
int sourceFlags, boolean parallel) {
this.previousStage = null;
this.sourceSupplier = source;
this.sourceStage = this;
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
// The following is an optimization of:
// StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
this.combined