上节介绍了将流转成集合的方法(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)

这个方法接受两个参数,一个是初始值,如果流中的元素都没有的时候就会返回这个identity,另一个参数是BinaryOperator,这个是指定具体做什么运算
比如下面这个reduce的用法可以计算流中所有元素的总和

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;@Overridepublic 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)

这个方法接受三个参数,除了Identity 和BinaryOperator以外,还有一个mapper,将流中的元素T转成U,然后BinaryOperator和Identity都是U类型的,相当于映射完之后再调上面介绍的第一个reducing方法,因此这个方法的返回类型是Collector<T, ?, U> ,输入流中元素类型为T,映射到U之后再做运算,最后的返回结果是U类型
具体实现是:

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()

其实也是借助了一个数组来实现,由于最终得到的结果是Integer,所以用一个长度为1的int数组来作为容器就行了
summingLong和summingDouble也是一样的道理,不过summingDouble要复杂一些,因为涉及到高八位和低八位的处理,所以用了长度为3的double数组,具体实现与Collectors的组成关联性不大,因此不做深入探究。

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

这个方法用到了长度为2的int数组,一个用来存放总值sum,另一个用来存放计数count,average = sum/count,因此接受的是ToIntFunction类型的参数,返回Double类型。
具体实现:

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也是类似的实现,在此省略。

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

  1. [Java8新特性]Collectors源码阅读-1 toCollections和joining等

    1.Collectors方法实现的种类 Collectors作为一个提供常用collector的静态方法类,里面的有很多公共的静态方法,大多数方法是借助new CollectorImpl来实现的,少数 ...

  2. Java8 Hashtable 源码阅读

    一.Hashtable 概述 Hashtable 底层基于数组与链表实现,通过 synchronized 关键字保证在多线程的环境下仍然可以正常使用.虽然在多线程环境下有了更好的替代者 Concurr ...

  3. Java8 ArrayBlockingQueue 源码阅读

    一.什么是 ArrayBlockingQueue ArrayBlockingQueue 是 GUC(java.util.concurrent) 包下的一个线程安全的阻塞队列,底层使用数组实现. 除了线 ...

  4. Java8 PriorityQueue 源码阅读

    一.什么是 PriorityQueue 这篇文章带大家去了解一个 jdk 中不常用的数据结构 PriorityQueue(优先队列),虽然在项目里用的不多,但是它本身的设计实现还是很值得大家看一看的. ...

  5. Java8 LinkedHashMap 源码阅读

    如果你对 HashMap 的源码有了解的话,只需要一图就能知道 LinkedHashMap 的原理了,但是具体的实现细节还是需要去读一下源码. 一.LinkedHashMap 简介 1.1 继承结构 ...

  6. 【java8新特性】——Stream API详解(二)

    一.简介 java8新添加了一个特性:流Stream.Stream让开发者能够以一种声明的方式处理数据源(集合.数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation ...

  7. 放大招了,肝了一篇8万字的Java8新特性总结,赶快收藏

    大家好,我是冰河~~ 说实话,肝这篇文章花了我一个月的时间,关于Java8的新特性全在这儿了,建议先收藏后阅读. Java8有哪些新特性? 简单来说,Java8新特性如下所示: Lambda表达式 函 ...

  8. java8 新特性精心整理(全)——新 Date/Time API

    https://blog.csdn.net/sanri1993/article/details/101176712 前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自 ...

  9. 【小家java】java8新特性之---Optional的使用,避免空指针,代替三目运算符

    相关阅读 [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小 ...

  10. 【java8新特性】——lambda表达式与函数式接口详解(一)

    一.简介 java8于2014年发布,相比于java7,java8新增了非常多的特性,如lambda表达式.函数式接口.方法引用.默认方法.新工具(编译工具).Stream API.Date Time ...

最新文章

  1. PHPRPC for PHP
  2. 字符流读取,乱码问题
  3. 查看redis aof内存_Redis持久化问题定位与优化技巧
  4. 数据库每日一题 2020.05.09
  5. Eclipse用法和技巧九:自动添加try/catch块2
  6. Codeforces Round #162 (Div. 2): D. Good Sequences(DP)
  7. 求解偏微分方程开源有限元软件deal.II学习--Step 2
  8. linux下搭建redis内网端口映射工具-rinetd
  9. 新中大财务软件-A3中怎样更改IP地址
  10. pdf不预览直接打印(笔记记录)
  11. 【蓝桥杯冲刺 day12】题目全解析
  12. 翻译 RFC 7322: RFC 样式指南
  13. 使用超级表格快速创建在线表单(如问卷调查、报名表)
  14. 相似剩余金额宝数值添加的动画
  15. 重装战姬服务器维护,重装战姬2020年9月10日更新维护公告重装战姬2020年9月10日更新了什么 - 量产资源网...
  16. DEM影像下载、拼接、裁剪(ArcGIS)
  17. 南大袁春风计算机系统基础(一)笔记
  18. Go开发之如何破解安装GoLand编译器?
  19. esxi虚拟机的显卡怎么来的_关于ESXI显卡直通(VmDirectPath),使虚拟机变成HTPC的若干经验...
  20. android App 集成 facebook 第三方认证登录

热门文章

  1. 从零开始制作点餐Android app(一)
  2. 路由器的四种配置模式
  3. Windows安装pytorch,傻瓜式教程
  4. pandas基础用法详解
  5. 如何将linux编译过程中的警告及错误信息输出到文件中
  6. Amazon Alexa硬件方案选型
  7. 为师弟师妹们连载(二)
  8. 用Python做数据分析之数据筛选及分类汇总
  9. 史上最简单的免费短信验证码案例
  10. flink sql读取kafka-入门级