java8学习总结——Collectors类源代码分析
来源:互联网 发布:网络招标 编辑:程序博客网 时间:2024/06/05 10:08
概述
在JDK8中,对Collectos类的定义:一个Collector接口的实现,并提供很多有用的方法。事实也是如此,通过Collectors提供的方法,我们可以完成大多数日常的集合运算操作。因此,Collectors类的所有方法返回都是Collector的实例。其实,Collectors使Collector更具体化。因为,Collector只是约束了一种操作运算的规范(提供容器,计算,归集,finisher得出最终结果),而Collectors则提供了各种各样的,具有具体功能的Collector。比如:toList(),、toMap、toSet()、joining()等等Collector,这些Collector都是有具体的功能的。
joining():将字符串元素拼接起来;
toSet():将操作的元素以Set集合形式输出;
toMap() :将操作元素以Map形式输出;
所以说,Collectors使Collector更加的具体化,简洁实用。
第一组:toSet、toMap、toCollectioin、toList、toConcurrentMap
这组Collector都是输出集合对象,它们的实现方式也大同小异,但是需要对Collector的实现方式很熟悉才可以很轻松看懂下面的源代码。
源代码:
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);}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);}public static <T> Collector<T, ?, Set<T>> toSet() { return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, (left, right) -> { left.addAll(right); return left; }, CH_UNORDERED_ID);}
从上面的代码中可以看出,toSet、toList、toCollection的实现方式都是通过创建ConllectorImpl来获得具体的Collector,创建的方式也很简单。首先创建一个集合容器;其次将元素放入集合容器中,最后进行归集操作conbiner,将多个结果集合合并为一个结果集合输出。由于输出类型与输入类型一致,也就没有finisher方法了。
只有map集合稍微复杂些。虽然toMap和toConcurrentMap也是通过创建CollectorImpl对象来实现的,但是由于Map集合的特殊性,所以在实现上有些微的差别。
源代码:
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);}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);}
从传入的参数上来看,toMap不仅有suppier方法,同时还有keyMapper和valueMapper两个Function,还有mergeFunctionh。前两个方法是用来分别获得存入map对象的key和value的。其传入参数是待操作的元素(element),而两个函数分别根据element获得key和value值。mergeFunction是用来将key和value值融入map集合中的。看一下map集合的merge方法的实现,或许可以更清晰一些。
源代码:
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;}这里的remappingFunction函数就是mergeFunction。从上面的源代码中可以看到,merge方法会先从map中根据当前key获取元素值oldValue,如果获取不到元素值oldValue,则直接将获得value值赋给newValue,然后放入集合中。如果能够获取到oldValue值,则将新的value和oldValue同时传递给remappingFunction函数,获得的返回值就是newVlaue。也就是map的新value值。将以上的操作封装成一个Biconsumer就得到了accumulator方法。
第二组:groupingBy,partioningBy
源代码:
Collectors创建Collector,将其具体化为某种行为。创建Collector需要五个参数,上面红框标注的分别是前四个参数supplier、accumulate、combiner、finisher。
classifer函数接收的是T类型返回的是K类型。K类型正好就是分组函数中的key的类型,然后根据key去获取容器,这里应用了computeIfAbsent方法。调用computeIfAbsent方法的是一个map对象,即m。computeIfAbsent的计算方式是:如果存在则返回,不存在则创建。也就是说,这个方法会为每一个唯一的key创建一个容器,并且将这个容器放入m的map中,这也就达到了根据key进行分组的目的。m的结构示意如下:
Map<key , Map<K , A>>
接下在再第二步时,通过mapMerge方法获得merger,即combiner。mapMerge方法的源代码如下:
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; };}里面调用了merge方法,merge方法会先判断m1中是否存在当前key的value值,即oldValue,如果存在,则使用mergeFunction将oldValue和m2中的value值进行汇集,并将结果放入m1;如果不存在,直接将key和value值放入m1中。
注: 这里只是解释这个mapMerger的运算过程,实际上在这里是不会执行这些运算的,这里只是将这一系列的运算方式封装成了merger,即Colletor中的combiner。
第三步没有太多的可说的,就是一个强制类型转换获得mangleFactory,即Collector中的supplier。这里解释一下为什么可以进行强制类型转换。
在理解完上面的groupingBy的源代码之后,再来看partitioningBy就简单很多了。
源代码:
第一步是创建了一个partition对象,这个对象包含两个属性forTrue和forFalse,在这里也分别创建了两个集合分别对应两个属性,这也符合partitionBy的本意,即将集合切分成true和false两个结果集合,根据给定的条件。
第二步也是创建了一个partition对象,分别应用downstream的combiner方法将true的结果集合和false的结果集合分别合并。然后就得到了两个结果集合。
第一个红框内,通过传入的predicate,如果predicate返回结果为true,则向downstreamaccumulate传入forTrue集合,认知则传入forFalse集合,以达到分组的目的。最后finisher方法的获取也是通过partition对象来完成的,方式很简单,就不一一赘述。partition的源代码如下:
private static final class Partition<T> extends AbstractMap<Boolean, T> implements Map<Boolean, T> { final T forTrue; //保存true类型的集合 final T forFalse; //保存false类型的集合 Partition(T forTrue, T forFalse) { this.forTrue = forTrue; this.forFalse = forFalse; } @Override public Set<Map.Entry<Boolean, T>> entrySet() { return new AbstractSet<Map.Entry<Boolean, T>>() { @Override public Iterator<Map.Entry<Boolean, T>> iterator() { Map.Entry<Boolean, T> falseEntry = new SimpleImmutableEntry<>(false, forFalse); Map.Entry<Boolean, T> trueEntry = new SimpleImmutableEntry<>(true, forTrue); return Arrays.asList(falseEntry, trueEntry).iterator(); } @Override public int size() { return 2; } }; } }
第三组:mapping,reducing
这两个方法辅助的性质更强一些。mapping方法可以转换Collector的接收元素类型。比如:Collector<T,A,D>的接收元素类型是T,通过mapping方法可以转换成为Collector<U,A,D>,这里的接收元素类型是U。
这是mapping方法的特点,所以mapping方法的参数是两个:第一个参数是类型转换方法Function<T,U>,接收一个T类型的参数,返回一个U类型的值; 第二个参数是Collector<T,A,D>的实例。
源代码:
public static <T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream) { BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator(); return new CollectorImpl<>(downstream.supplier(), (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)), downstream.combiner(), downstream.finisher(), downstream.characteristics());}从源代码中可以看到,Collector的在accumulate的接收参数之前,先应用了mapper的方法,然后将返回值输入给accumulate。以此来达到转换Collector接收类型的目的。
至于reducing方法,这个方法是将BinaryOperator操作转换成Collector接口实例,然后应用到Collector接收的每个元素上。
源代码:
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) { return new CollectorImpl<>( boxSupplier(identity), (a, t) -> { a[0] = op.apply(a[0], t); }, (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; }, a -> a[0], CH_NOID);}reducing接收的两个参数,第一个参数(identity),是一个默认值,当没有输入元素时,将会输出identity。第二个参数就是BinaryOperator操作。reducing方法更多的是被summingInt、summingDouble、summingLong等方法调用,用于计算数字的总和。
- java8学习总结——Collectors类源代码分析
- java8中Collectors的学习
- java8学习总结——Collector接口
- Java8—ConcurrentHashMap分析
- Java8新特性之Collectors
- Java8新特性之Collectors
- Java8新特性之Collectors
- java8学习总结——函数式接口@FunctionalInterface
- CASSINI源代码分析(5)——总结
- java8学习总结1
- java8学习总结2
- Java8学习总结
- Java8学习总结
- Java8 Collectors其它一些方法的使用
- Java8学习笔记 — Optional
- Java8新特性学习总结
- Java8 新特性学习总结
- Collectors
- Golang基于redis实现的分布式信号量(semaphore)
- vue基础案例2
- tablayout+viewpager联动
- 【回顾】协同·组织管理新时代——致远互联执行总裁向奇汉
- 投资收购微盟、博易智软后,要做中国Salesforce+ Palantir的天马股份再发智能商业π型战略
- java8学习总结——Collectors类源代码分析
- 给个n,求1到n的所有数的约数个数的和~
- 吴恩达联手富士康打造AI技术 | 京东云与百分点达成战略合作 | 外媒认为比特币疯涨的幕后推手是日本人
- 使用Easy UI进行页面的简单布局
- 我想写什么样的代码
- 全差分运放浅析
- 单链表的创建,删除,插入,以及合并
- 【重大改革】吴恩达:孩子会识字后立马教她Python!
- 安卓第三方登录集成