1 函数式接口的分类
常见的函数式接口可以大致分为以下几类:
单输入,无输出
单输入,单输出
无输入,单输出
两个不同类型的输入,第三种类型的输出
两个不同类型的输入,其中一种类型的输出
……
可以发现,无非以下三种大的分类:
供给型:生产数据,一般无输入
消费型:消费数据,一般无输出
转换型:TypeA —> TypeB的类型转换
在实际使用中,没必要每次都新建这些函数式接口来支持lambda,JDK对各种类型的函数式接口已经基本都内置了。这些内置函数式接口都在 java.util.function 包下。
1.1 供给型
Supplier
1.2 消费型
Consumer
1.3 转换型
Function
1.4 Predicate (特殊的转换型接口)
Predicate
2 Stream
java8最吸引人的地方之一就是牛逼哄哄的 Stream-API 了。简单理解他就是升级版的 Iterator,比迭代器强大多了。
Stream
函数式编程关注的是对纯数据的处理,对于 Stream 的操作大致就是三个过程:
StreamProcess
本节注重于 Stream 的 收集 操作,对于数据源和中间的处理过程略过。
2.1 Stream的获取
略过。
2.2 Stream的中间操作
略过。
2.3 Stream.collect()
对于Stream最终的收集操作有两个重载的版本:
R collect(Collector super T, A, R> collector);
R collect(Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner);
要理解这两个方法,先看看 Collector 的几个方法。
3 Collector
/**
* @param Stream中的元素类型
* @param 收集过程中的临时中间类型
* @param 收集完成后输出结果的类型
**/
public interface Collector {
/**
* 提供一个结果容器。
*/
Supplier supplier();
/**
* 两个参数的消费型接口。将迭代中的当前元素添加到结果容器中。
*/
BiConsumer accumulator();
/**
* 转换型接口,两个相同输入,同类型的输出。
* 在并行处理中,将各个子Stream返回结果合并。
*/
BinaryOperator combiner();
/**
* 收集操作的最后一步。
* 转换型接口,用于将收集过程中的临时中间类型元素(A)转换为结果类型(R)。
*/
Function finisher();
/**
* 返回一个集合。描述了被收集的流的一些特性。
* 比如:是否是顺序相关的、支不支持并行等。
* 详情见:3.4 Characteristics
*/
Set characteristics();
}
3.1 Collector.collect()源码
源码中是这样的:
public final R collect(Collector super P_OUT, A, R> collector) {
A container;
if (
// 并行流(stream().parallel()方法被调用)的情况
isParallel()
&&
// 优化提示中包含 CONCURRENT 属性
(collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&&
// 流是无序的
(!isOrdered()
||
// 优化提示中包含 UNORDERED 属性
collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
}
else {
container = evaluate(ReduceOps.makeRef(collector));
}
return
// 优化提示中包含 IDENTITY_FINISH(恒等函数) 属性?
collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
?
// 直接将中间临时元素(A)当做最终结果(R)
(R) container
:
// 使用 finisher 转换成最终结果
collector.finisher().apply(container);
}
从以上源码可以大致得出下面的两个流程图:
3.2 串行流的执行过程
sequential-stream
3.3 并行流的执行过程
parallel-stream
3.4 Characteristics
该类主要是给 Collector 接口在 collect() 的时候提供一些优化参数。
enum Characteristics {
/**
* 支持多线程调用,并行收集。
*/
CONCURRENT,
/**
* 流中元素是无序的,收集过程不受元素先后顺序的影响。
*/
UNORDERED,
/**
* 恒等函数。
* 此时不再使用finisher再转换一次元素。直接将中间元素(A)当做最终结果(R)。
*/
IDENTITY_FINISH
}
这三个特性的常用组合在 Collectors 工具类中也有定义:
static final Set CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set CH_NOID = Collections.emptySet();
对于流的收集操作先介绍这点,下次分享自定义Collector。