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