Stream中的数据处理
接口Stream类似于一个迭代器,但提供了更为丰富的操作,Stream API的主要操作就定义在该接口中。Java 8给Collection接口增加了两个默认方法,它们可以返回一个Stream,如下所示:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
stream()返回的是一个顺序流,parallelStream()返回的是一个并行流。
顺序流就是由一个线程执行操作。
并行流内部会使用多线程,线程个数一般与系统的CPU核数一样,以充分利用CPU的计算能力;使用并行流不需要显式管理线程,使用方法与顺序流是一样的;并行流内部会使用Java 7引入的fork/join框架,即处理由fork和join两个阶段组成,fork就是将要处理的数据拆分为小块,多线程按小块进行并行计算,join就是将小块的计算结果进行合并。
优点
- 没有显式的循环迭代,循环过程被Stream的方法隐藏了。
- 提供了声明式的处理函数,比如filter,它封装了数据过滤的功能,而传统代码是命令式的,需要一步步的操作指令。
- 流畅式接口,方法调用链接在一起,清晰易读。
中间操作和终端操作
像filter和map这种不实际触发执行、用于构建流水线、返回Stream的操作称为中间操作(intermediate operation),而像collect这种触发实际执行、返回具体结果的操作称为终端操作(terminal operation)。
类型 | 函数 | 作用 | 说明 |
中间操作 | filter | 数据过滤 |
|
map | 数据转换 | ||
distinct | 数据去重 |
| |
sorted | 数据排序 |
| |
skip | 跳过流中的n个元素 |
| |
limit | 限制流的长度 |
| |
peek | 传递给自定义的consumer |
| |
mapToLong | 转为Long值 | 为避免装箱/拆箱,提高性能,Stream还有这几个返回基本类型特定流的方法。 | |
mapToInt | 转为Int值 | ||
mapToDouble | 转为Double值 | ||
flatMap | 完成了一个1到n的映射 | 它接受一个函数mapper,对流中的每一个元素,mapper会将该元素转换为一个流Stream,然后把新生成流的每一个元素传递给下一个操作。比如:
| |
终端操作 | collect | 强大的收集器 | 实现功能:
|
max | 求最大值 | 它们的返回值类型是Optional<T>,而不是T。 | |
min | 求最小值 | ||
count | 返回元素个数 | ||
allMatch | 所有都满足 |
| |
anyMatch | 任意一个满足 | ||
noneMatch | 都不满足 | ||
findFirst | 返回第一个 |
| |
findAny | 返回任意一个 | ||
forEach | 挨个遍历 |
| |
toArray | 转为数组 | ||
reduce | 规约(聚合为一个值) |
收集器
Collector,这是一个接口,它的定义基本上是:
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
}
在顺序流中,collect方法与这些接口方法的交互大概是这样的:
//首先调用工厂方法supplier创建一个存放处理状态的容器container,类型为A A container = collector.supplier().get(); //对流中的每一个元素t,调用累加器accumulator,参数为累计状态container和当前元素t for(T t : data) collector.accumulator().accept(container, t); //最后调用finisher对累计状态container进行可能的调整,类型转换(A转换为R),返回结果 return collector.finisher().apply(container);
combiner只在并行流中有用,用于合并部分结果。
characteristics用于标示收集器的特征,Collector接口的调用者可以利用这些特征进行一些优化。
Characteristics是一个枚举,有三个值:CONCURRENT、UNORDERED和IDENTITY_FINISH。
Collectors.toList()具体是什么呢?看下代码:
public static <T>
Collector<T, ? , List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) ->
{ left.addAll(right); return left; },
CH_ID);
}
#函数式编程##java原理#知其然知其所以然,只有掌握了底层原理,借助第一性原理,才可以在日常开发和项目中运用自如,潇洒走江湖。
查看11道真题和解析