Java8 Stream源码精讲(一):从一个简单的例子入手

简介

Java8引入Stream这个新特性之后,通过使用lambda表达式增强集合的功能,使程序员通过声明式的方式,快速和便捷的对批量数据进行过滤、转换、分组规约等操作,同时提高编程效率和代码可读性,可以说是一个真正的开发利器。本章我通过一个简单的示例,带领大家理解Stream的内部原理。

继承体系

在进入示例之前,我们先来看一下Stream的类继承结构。

图中展示的是ReferencePipeline的继承关系,Stream和ReferencePipeline都是针对引用类型定义的接口和类,还有针对int、long、double基础类型的流IntStream、LongStream、DoubleStream、IntPipeline、LongPipeline和DoublePipeline。本章只打算讲解引用类型的流,也就是大家常用的Stream,后面会有专门的章节来讲解基础类型的流,下面大致说明一下各个类和接口的职责功能。

  • BaseStream: 所有Stream都继承自这个接口,主要声明了并行流和串行流的转换,判断流类型等方法。
  • Stream: 继承BaseStream接口,提供了所有引用类型的中间操作和终止操作方法,什么是中间操作和终止操作,后面会详细讲解。
  • PipelineHelper: 流管道的帮助类,定义了管道的基础方法,由AbstractPipeline实现。
  • AbstractPipeline: 流管道基础类,继承PipelineHelper并且实现了继承BaseStream接口接口,所有的流都继承这个类,内部通过双向链表的结构来维护管道之间的关系。
  • ReferencePipeline: 引用类型的管道类,继承了AbstractPipeline,连接管道的能力。同时实现了Stream接口,所以具备相应的流操作功能。
  • Head: ReferencePipeline的内部类,同时也继承自ReferencePipeline,构建的Stream对象其实就是这个类的实例。
  • StatelessOp: 同样是ReferencePipeline的内部类,并且继承了ReferencePipeline,调用流的无状态操作方法会返回这个类的子类实例。
  • StatefulOp: 同上,不过表示有状态操作。

下面我们来看看顶层接口和类的方法和字段声明,本章只粗略的讲解BaseStream、PipelineHelper和AbstractPipeline,其它的留到后面的章节详细讲解。

BaseStream

//返回流代表集合的迭代器
Iterator<T> iterator();

//返回流元素的Spliterator,什么是Spliterator,翻译成中文是分离器,
//目前大家只需要知道这是一个类似迭代器的东西,主要用于终止操作的时候遍历元素
Spliterator<T> spliterator();

//返回流是不是并行的
boolean isParallel();

//返回串行流
S sequential();

//返回并行流
S parallel();
复制代码

PipelineHelper

//返回流类型,有REFERENCE、INT_VALUE、LONG_VALUE、DOUBLE_VALUE类型
abstract StreamShape getSourceShape();

//返回流合并和操作的标志,这个过于复杂,不打算深讲
abstract int getStreamAndOpFlags();

//返回流元素大小,如果不确定将返回-1
abstract<P_IN> long exactOutputSizeIfKnown(Spliterator<P_IN> spliterator);

//由终止操作间接调用,调用wrapSink方法构建Sink链表,调用copyInto方法完成数据操作
abstract<P_IN, S extends Sink<P_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator);

//将Spliterator中的元素提供给Sink消费
abstract<P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);

//类似copyInto,区别在于这个方法用于短路操作
abstract <P_IN> void copyIntoWithCancel(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);

//包装Sink形成Sink链
abstract<P_IN> Sink<P_IN> wrapSink(Sink<P_OUT> sink);

abstract<P_IN> Spliterator<P_OUT> wrapSpliterator(Spliterator<P_IN> spliterator);

abstract Node.Builder<P_OUT> makeNodeBuilder(long exactSizeIfKnown,
                                             IntFunction<P_OUT[]> generator);

//并行流调用,本系列文章不涉及
abstract<P_IN> Node<P_OUT> evaluate(Spliterator<P_IN> spliterator,
                                    boolean flatten,
                                    IntFunction<P_OUT[]> generator);
复制代码

AbstractPipeline

//pipeline链表的头结点
private final AbstractPipeline sourceStage;

//前一个节点
private final AbstractPipeline previousStage;

protected final int sourceOrOpFlags;

//下一个节点
private AbstractPipeline nextStage;

//节点的深度
private int depth;

private int combinedFlags;

//源Spliterator
private Spliterator<?> sourceSpliterator;

//同sourceSpliterator
private Supplier<? extends Spliterator<?>> sourceSupplier;

//pipeline是否已经被连接或者消费
private boolean linkedOrConsumed;

//pipeline上是否有有状态的操作
private boolean sourceAnyStateful;

private Runnable sourceCloseAction;

//是否是并行流
private boolean parallel;
复制代码
  • sourceStage:pipeline链表的头结点,如果当前节点就是头节点,那么将指向自己。
  • previousStage:链表的上一个节点。
  • nextStage:链表的下一个节点。
  • depth:节点的深度,头结点深度为0,后续每调用一个中间操作,返回的Stream节点深度加1。
  • sourceSpliterator:源Spliterator。
  • sourceSupplier:同sourceSpliterator。
  • linkedOrConsumed:pipeline是否已经被连接或者消费,如果为true,再调用操作方法将抛出IllegalStateException异常。
  • sourceAnyStateful:pipeline上是否有有状态的操作,如果有调用有状态中间操作,那么头结点的sourceAnyStateful会被设置为true。

一个简单的示例

我们先来看一个使用Stream的例子

Stream.of("java", "scala", "go", "python")
        .map(String::length)
        .filter(len -> len <= 4)
        .forEach(System.out::println);
复制代码

很显然,例子中的运行结果是在控制台输出4和2:

4
2
复制代码

上面的代码中通过Stream#of()方法创建一个源Stream实例,然后依次调用map()、filter()中间操作方法,最终调用forEach()终止操作方法得到最终的结果。其中中间操作不会立即执行声明的逻辑,只有调用终止操作之后才触发所有的逻辑。

Stream的构建

我们先来看一下这个例子中Stream是如何构建的,方法Stream#of():

public static<T> Stream<T> of(T... values) {
  return Arrays.stream(values);
}
复制代码
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾听铃的声

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值