上一小节基本的函数式接口以及函数式思维进行了讲解,为了本节进行基础打底,当然这个是必要的,这里面的静态方法还是有些理解难度的(某几个),本节将对Collectors工厂类方法一部分方法进行分析,由于方法较多,所以需要分开进行细致的刨析。ok,长话短说,进行今天的分析之旅吧。
private static <T> BinaryOperator<T> throwingMerger() {
return (u,v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u)); };
}
返回一个合并函数,适合在Map的merge(Object,Object,BiFunction)Map.merge()}或toMap(function,function,BinaryOperator)toMap()}中使用,该函数始终抛出 IllegalStateException。这可以用来强制假设所收集的元素是不同的。
该代码可以理解为如此:
BinaryOperator<T> binaryOperator=(u,v)
-> {
throw new IllegalStateException(String.format("Duplicate key %s", u)); };
其实就是一个实现了带调用的变量,当然函数式称之为行为,下面还有基本都是如此的静态方法,就不一一这样每一个都举例出变量的形式来理解了,分析方式一致,与 throwingMerger();
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
此方法将泛型I强制转换成R,当然在转换之前需要进行类型检验,以免发生强制类型转换异常;
private static <K, V, M extends Map<K,V>>
BinaryOperator<M> mapMerger(BinaryOperator<V> mergeFunction) {
return (m1, m2) -> {
for (Map.Entry<K,V> e : m2.entrySet())
m1.merge(e.getKey(), e.getValue(), mergeFunction);
return m1;
};
}
这里需要进行转换分析,这是一个比上面来说更复杂的高阶函数,当然这里还有没有进行函数复合,只是一个函数式接口实现,等价为如下代码:
BinaryOperator<M> binaryOperator=(m1, m2) -> {
for (Map.Entry<K,V> e : m2.entrySet()){
m1.merge(e.getKey(), e.getValue(), mergeFunction);
}
return m1;
};
首先先分析BinaryOperator,接口内唯一的抽象方法R apply(T t, U u);接收两个参数,返回一个参数(当然这里i就是m1,如果binaryOperator被执行的话),当然,代码实现里进行了mergeFunction函数返回值的切割,结果会在m1里面并进行返回操作。
private static <T> Supplier<T[]> boxSupplier(T identity) {
return () -> (T[]) new Object[] {
identity };
}
将identity放入到T[]里面,生成T类型的数组,但是泛型不可以直接创建数组,所以需要new Object[]生成出来,在进行强转。
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);
}
方法返回一个 Collector,它将输入元素累加到一个新的 List中。对于返回的 List的类型、可变性、serializability或线程安全性没有保证;如果需要对返回的List进行更多控制,请使用oCollection(Supplier)。
返回值为收集器接口Collector(收集器的实现的返回参数为集合)的静态方法基本都是这样的实现思路,所以就以第一个开刀进行刨析,我会分层给大家分析清楚这块代码究竟做了什么?
StreamAPI源码分析之二(Collectors工厂类内部设计分析篇)链接,我们介绍了Collectors内部设计,其中静态内部类CollectorImpl进行了分析,也是对本节的基础打底,如果没有看过这一篇的可以补充下基础知识,现在理解方法觉得没有理解苦恼的,我们继续往下分析。
这里new CollectorImpl<>是静态内部类在当前类里的静态方法里的创建方式,而CollectorImpl本来有两个构造方法(四参和五参),这里使用的是四参的构造函数。
现在对构造函数中的每个参数的实现方式进行分析:
ArrayList::new;//方法引用的形式
()->{
return new ArrayList();}//lambda形式
两个是等价的
List::add;//方法引用的形式
(item,list)->{
list.add(item);}//lambda形式
两者是等价的,只是方法引用的形式隐藏了一些细节
(left, right) -> {
left.addAll(right); return left; }://lambda形式
它的代替形式
List::addAll;//方法引用的形式
两者等价(如果addAll返回处理完成的集合的话,但是这里不是集合所以这里方法引用行不通)
但是List的addAll返回值是boolean,这里需要返回一个集合本身,所以这里不能直接使用方法引用,所以源码使用的是lambda形式的
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);
}
与下面这个是等价的,都是对Collector
Collector<T, ?, List<T>> collector=new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> {
left.addAll(right); return left; },
CH_ID);
接下的一些和 toList()类似,就不费大量的时间来分析了,分析思路一致。
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
(left, right) -> {
left.addAll(right); return left; },
CH_UNORDERED_ID);
返回一个Collector,它将输入元素累加到一个新的Set。对于返回的Set的类型、可变性、serializability或线程安全性没有保证;
简要分析:
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}
当然还有两个重载的toMap方法,但是两个最终还是都是调用了上面的这个toMap方法,此为toMap的具体实现细节。
/*
* @since 1.8
*/
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
这个是JDK1.8Map新增的默认方法,它的作用就是将传入的key获取到它的值
1、如果oldValue 不存在,就是用参数传入的value,如果newValue 被value赋值后,依然为空,就将key移除,
或者为key设置新的value
2、如果oldValue 存在,就好执行mergeFunction函数的apply方法,处理oldValue以及新传入的value,执行结果要看传入
的BiFunction具体实现了,BiFunction是我们外部调用者可控制的。
private static <K, V, M extends Map<K,V>>
BinaryOperator<M> mapMerger(BinaryOperator<V> mergeFunction) {
return (m1, m2) -> {
for (Map.Entry<K,V> e : m2.entrySet())
m1.merge(e.getKey(), e.getValue(), mergeFunction);
return m1;
};
}
上面的源码可以理解为这样的变形,调用这个方法mapMerger(BinaryOperator mergeFunction)等于下面:
BinaryOperator<M> mapMerger=(m1, m2) -> {
for (Map.Entry<K,V> e : m2.entrySet())
m1.merge(e.getKey(), e.getValue(), mergeFunction);
return m1;
重点就是M泛型就是我们需要返回的元素,也就是最终返回的集合类型,而上面的方法中,BinaryOperator的父类BiFunction
这个时候收集器的实现就已经完成了,等待调用了可以。
public static <T, K, U, M extends ConcurrentMap<K, U>>
Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_CONCURRENT_ID);
}
返回一个并发的 Collector,它将元素累加到@code ConcurrentMap中,该映射的键和值是将提供的映射函数应用于输入元素的结果。
toConcurrentMap与toMap在CollectorImpl的第二个参数、第三个参数是一模一样的实现方式,就不多做分析了,这里说一下第一个参数和最后一个参数.。
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> {
r1.addAll(r2); return r1; },
CH_ID);
}
返回一个Collector,它按遇到顺序将输入元素累积到一个新的 Collection。Collection由提供的工厂创建。
这个toCollection方法主要目的是满足我们需要的数据结构,除了上面的set、List、Map、ConcurrentMap之外的数据结构作为容器
返回一个Collector,它将输入元素以指定的分隔符分隔,并使用指定的前缀和*后缀按遇到顺序连接起来。
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge,
StringJoiner::toString, CH_NOID);
}
具体实现里,首先映入眼帘的就是StringJoiner这个字符串类,当然我这里就不分析它了,这里就是对joining()方法进行整体上的分析,想要了解StringJoiner这个JDK1.8新增加的String类,可以去看一下。
在这里再强调一下,本小节是带着大家学习函数式思维的,以及分析能力,而不是见什么都要的,当然 joining() 还有两个重载方法,都是最终调用了当前方法,所以就不去分析了。
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
public static <T, U>
Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> {
a[0] = op.apply(a[0], mapper.apply(t)); },
(a, b) -> {
a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0], CH_NOID);
}
返回Collector接受类型为T的元素,该类型计算输入元素的数量。如果不存在元素,结果为0。
这个里面设计到reducing()函数。
分析reducing三个参数:
reducing具体实现的 CollectorImpl五个参数为
@SuppressWarnings("unchecked")
private static <T> Supplier<T[]> boxSupplier(T identity) {
return () -> (T[]) new Object[] {
identity };
}
(a, t) ->{
a[0] = op.apply(a[0], mapper.apply(t)); };
相当于
Object[] a=new Object[0];
a[0]=a[0]+t;
(a, b) -> {
a[0] = op.apply(a[0], b[0]); return a; }:
等价于
Object[] a=new Object[0];
a[0]=a[0]+t;
Object[] b=new Object[0];
b[0]=b[0]+t;
a[0]=a[0]+b[0]
reture a[0];
这个时候 CollectorImpl已经完成了,可以进行外部使用。
reducing()还有一个重载的方法,这里就不进行分析了,最终的实现过程和上面reducing()基本一致。
还有一些进行计算的函数式方法如summingInt()、summingLong()、averagingInt()等等,其实他们实现方式都是分步骤进行的,只是已经进行实现了而已,都是如下步骤:
所以其他的计算方法就不在这里进行分析了,接下来的一小节即将对比较复杂的分组和分区进行深入分析,欢迎观看。