Stream🚩
Stream提供了对Java集合的一种全新的操作方式,由于简约的风格,以及不错的性能,已经成为Java开发人员必须掌握的基本技能。下面直观地感受一下Stream流的用法:
Stream快速开始
// 排序
numbers = numbers.stream().sorted(Integer::compareTo).collect(Collectors.toList());
// 根据ID去重
List likeTidList = likeDOs.stream().map(LikeDO::getTid)
.distinct().collect(Collectors.toList());
前置知识:lambda表达式概述
由于Stream依赖lambda表达式,因此先做一些铺垫:lambda表达式为何能作为方法的入参?要点在于:函数式接口。函数式编程是对行为抽象
函数式接口
定义:只有一个抽象方法的接口。
即使接口还包含其它默认方法default和静态方法static,只要抽象方法只有一个,就行
比如最常见的:Runnable
// 函数式接口注解
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
lambda表达式
lambda表达式是对函数式接口的实现。在没有lambda的时代,我们往往会用匿名函数,实现接口的方法。但是这样做代码量大,复杂。lambda表达式的特点就是极其轻量化,能简则简。
Runnable runnable = () -> { //代码更加简洁
System.out.println("Hello world!");
};
lambda的原理
关键点在于对字节码的分析,可以参考:
Java Lambda表达式原理解析
总之,Lambda表达式会被编译生成一个私有的静态函数,并且生成一个实现了函数式接口的内部类。在这个内部类的唯一的抽象方法中,会调用生成的私有的静态函数来实现该唯一的抽象方法。
下面正式开始介绍Stream的实现原理。流的原理基本可以划分为三个部分:流的创建,中间操作,以及终止操作,我们逐个来看。
一、Stream的创建
list.stream()
这是获取一个流对象,但我们要知道,流对象到底是什么?有什么核心字段?如何存储数据源?有多少种方式创建流对象?
Stream的创建方式
1、Stream.of(T... values)
public static Stream of(T... values) {
return Arrays.stream(values);
}
// 本质是Arrays.stream,实质就是:
return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
// 又是StreamSupport.stream
2、Collection.stream()
也就是所有Collection的子类都可以,包括ArrayList,ArrayDeque
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
3、Arrays.stream(T [])
数组当然也支持,前文也说过本质还是StreamSupport.stream
StreamSupport:真正创建Stream对象
来看看它的stream方法
// StreamSupport.stream
public static Stream stream(Spliterator spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
return new ReferencePipeline.Head (spliterator,StreamOpFlag.fromCharacteristics(spliterator),parallel);
}
可以看到,返回的实际上是ReferencePipeline的内部类Head(实现了Stream接口) 。
并且传递了一个参数Spliterator。那么这两个类就是分析的重点
Spliterator:数据源的载体
Spliterator的中文翻译过来是分离器,但是这并不能很好的帮助你理解Spliterator。
Spliterator最大的意义是:替Stream存储数据源,处理数据源,辅助并行操作。
这里以数组为例,ArraySpliterator来分析:(各种Spliterator实例都被封装在Spliterators类的内部类中)
1、存数源数据
// 数组获取spliterator的方法
return Spliterators.spliterator(array, startInclusive, endExclusive,...);
// Spliterators.spliterator方法
public static Spliterator spliterator(Object[] array, int fromIndex, int toIndex,
int additionalCharacteristics) {
return new ArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
}
static final class ArraySpliterator implements Spliterator{
private final Object[] array;
private int index; // current index, modified on advance/split
private final int fence; // one past last index
private final int characteristics;
}
这就可以看到Spliterator的一个作用:存数源数据。
2、处理数据的能力
然后再来看看它的核心方法:
// lambda表达式,消费一个元素
public boolean tryAdvance(Consumer source,
int sourceFlags, boolean parallel) {
this.previousStage = null;
// 可以看到 用 source 命名 Spliterator
// 而在下面两个的构造方法是没有赋值sourceSpliterator的
this.sourceSpliterator = source;
this.sourceStage = this;
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
this.combinedFlags = (~(sourceOrOpFlags upstream) {
return new OfRef(upstream);
}
发现,返回的是个OfRef,而OfRef正是StatefulOp的一个子类
private static final class OfRef extends ReferencePipeline.StatefulOp {
// isNaturalSort表示元素本身是否可以排序(传参数不传比较器则为true,否则为false)
private final boolean isNaturalSort;
private final Comparator