[Java8新特性]Collectors源码阅读-2 reducing,maxBy,summingInt等

[Java8新特性]Collectors源码阅读-2 reducing,maxBy,summingInt等上节介绍了将流转成集合的方法 toCollection 和将元素转换成拼接字符串 joining 的方法 本节介绍几个求统计数值的方法 分别是 counting maxBy minBy summingInt summingLong summingDoubl

大家好,我是讯享网,很高兴认识大家。

上节介绍了将流转成集合的方法(toCollections)和将元素转换成拼接字符串(joining)的方法,本节介绍几个求统计数值的方法,分别是

  1. counting
  2. maxBy
  3. minBy
  4. summingInt/summingLong/summingDouble
  5. averagingInt/averagingLong/averagingDouble
  6. reducing

前三个都是借助reducing方法来实现的,后面的两个方法的实现思路和reducing很像,所以这节先介绍reducing铺垫一下

1.reducing

reducing方法可以将流中的元素进行累加,运算到第n个元素的时候,将前n-1个元素运算的中间结果和第n个元素进行运算,这个方法有三个重载

1.1reducing(T identity, BinaryOperator op)

int total = IntStream.range(0, 10).reduce(0, Integer::sum); 

讯享网

返回的类型是Collector<T, ?, T>,即流中的元素和最终输出的元素是同一个类型T,中间容器的类型可以是任意
这个方法的源码是:
具体实现是:

讯享网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); 

可以看到,它借助了一个长度为1的数组来作为中间的结果容器,这个的方法boxSupplier其实是提供这个数组,它的实现是:

private static <T> Supplier<T[]> boxSupplier(T identity) { 
    return () -> (T[]) new Object[] { 
    identity }; } 

只是提供了一个类型为T的长度为1的数组,数组里面的元素是identity,也就是传入的第一个参数,这个collector具体的组成是

supplier accumulator combiner finisher characteristic
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
长度为1,类型为T,初始值为identity的数组 是将当前的元素T和中间结果a[0]进行运算之后再重新赋值到a[0] 对于两个结果装着中间结果的数组,将他们的值进行运算之后再返回 返回这个a[0]的值 Collections.emptySet()

为什么要借助这个数组,直接传一个值一直做运算不行吗?有这个疑惑的话需要考虑一下值对象是没办法做传递的,引用的对象才能将应用传递,这样这个对象的之才能修改,数组是一种引用对象,可以将地址传递,能在运算的时候对里面的值进行改变

1.2 reducing(BinaryOperator op)

这个方法接受一个参数,BinaryOperator,指定具体做什么运算,返回的类型是 Collector<T, ?, Optional>
也就是说,流中的元素是T,返回的结果是一个Optional,中间容器的类型可以是任意
这个方法的实现是借助了一个内部类OptionalBox,进行Optional类型的封装,这个方法已经集成了参数BinaryOperator op,进行accept方法时,如果是当前值是存在的就会进行运算

讯享网class OptionalBox implements Consumer<T> { 
    T value = null; boolean present = false; @Override public void accept(T t) { 
    if (present) { 
    value = op.apply(value, t); } else { 
    value = t; present = true; } } } 

这个方法的具体实现是:

return new CollectorImpl<T, OptionalBox, Optional<T>>( OptionalBox::new, OptionalBox::accept, (a, b) -> { 
    if (b.present) a.accept(b.value); return a; }, a -> Optional.ofNullable(a.value), CH_NOID); 

返回的Collector的组成是:

supplier accumulator combiner finisher characteristic
OptionalBox::new OptionalBox::accept (a, b) -> { if (b.present) a.accept(b.value); return a; } a -> a[0] CH_NOID
提供一个OptionalBox的容器 调用对象的accept方法,accept里面包含BinaryOperator的运算了 对于两个结果装着中间结果的OptionalBox,将他们的结果进行运算之后再返回 返回这个a[0]的值 Collections.emptySet()

1.3 reducing(U identity, Function<? super T, ? extends U> mapper,BinaryOperator 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); 

在第一个重载方法的基础上加了一个映射,从a[0] = op.apply(a[0], t)变成了a[0] = op.apply(a[0], mapper.apply(t));

以上就是reducing三个重载方法的详解,知道了这些内容,下面的几个方法就很容易理解了,先介绍三个借助它来实现的方法,分别是counting,maxBy,minBy,然后再介绍两个跟它的实现思路很像的方法summingInt, averagingInt,还有summingLong,averagingLong等是同理的,不会详细说明


讯享网

2.counting()

这个方法的实现是

public static <T> Collector<T, ?, Long> counting() { 
    return reducing(0L, e -> 1L, Long::sum); } 

直接通过reducing的第三个重载方法实现,identity是0,map方法是e -> 1L,每个对象都会转换成1L,运算的方法是Long的加法,在这里T是流中的元素,U的类型是Long

3.minBy()

这个方法借助reducing的第二个重载方法返回一个,借助BinaryOperator.minBy(comparator)方法,这个方法接受一个comparator,也是返回一个BinaryOperator

讯享网public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator) { 
    return reducing(BinaryOperator.minBy(comparator)); } 

BinaryOperator.minBy(comparator)的实现是:

public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { 
    Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } 

所以collectors的minBy()方法相当于:

讯享网public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator) { 
    Objects.requireNonNull(comparator); return reducing((a, b) -> comparator.compare(a, b) <= 0 ? a : b;); } 

reducing接受的参数很明显是一个BinaryOperator,接受两个参数,返回更小的那一个

4.maxBy()

类似minBy,不过多赘述

5.summingInt(ToIntFunction<? super T> mapper)

该方法接受一个ToIntFunction方法,返回Integer,实现是:

public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper) { 
    return new CollectorImpl<>( () -> new int[1], (a, t) -> { 
    a[0] += mapper.applyAsInt(t); }, (a, b) -> { 
    a[0] += b[0]; return a; }, a -> a[0], CH_NOID); } 

ToIntFunction顾名思义,就是接受泛型T,然后返回一个int的方法

讯享网int applyAsInt(T value); 

所以返回的Collector的组成是:

supplier accumulator combiner finisher characteristic
() -> new int[1] (a, t) -> { a[0] += mapper.applyAsInt(t); } (a, b) -> { a[0] += b[0]; return a; } a -> a[0] CH_NOID
提供一个长度为1的int数组 调用ToIntFunction的applyAsInt方法,将这个结果与数组中的结果相加 对于两个装着中间结果的数组,将他们的结果进行运算之后再返回 返回这个a[0]的值 Collections.emptySet()

6. averagingInt(ToIntFunction<? super T> mapper)

public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper) { 
    return new CollectorImpl<>( () -> new long[2], (a, t) -> { 
    a[0] += mapper.applyAsInt(t); a[1]++; }, (a, b) -> { 
    a[0] += b[0]; a[1] += b[1]; return a; }, a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID); } 

averagingLong和averagingDouble也是类似的实现,在此省略。

小讯
上一篇 2025-03-29 20:22
下一篇 2025-04-10 14:04

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/43221.html