[Java8新特性]Collectors源码阅读-2 reducing,maxBy,summingInt等
上节介绍了将流转成集合的方法(toCollections)和将元素转换成拼接字符串(joining)的方法,本节介绍几个求统计数值的方法,分别是
- counting
- maxBy
- minBy
- summingInt/summingLong/summingDouble
- averagingInt/averagingLong/averagingDouble
- 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等相关推荐
- [Java8新特性]Collectors源码阅读-1 toCollections和joining等
1.Collectors方法实现的种类 Collectors作为一个提供常用collector的静态方法类,里面的有很多公共的静态方法,大多数方法是借助new CollectorImpl来实现的,少数 ...
- Java8 Hashtable 源码阅读
一.Hashtable 概述 Hashtable 底层基于数组与链表实现,通过 synchronized 关键字保证在多线程的环境下仍然可以正常使用.虽然在多线程环境下有了更好的替代者 Concurr ...
- Java8 ArrayBlockingQueue 源码阅读
一.什么是 ArrayBlockingQueue ArrayBlockingQueue 是 GUC(java.util.concurrent) 包下的一个线程安全的阻塞队列,底层使用数组实现. 除了线 ...
- Java8 PriorityQueue 源码阅读
一.什么是 PriorityQueue 这篇文章带大家去了解一个 jdk 中不常用的数据结构 PriorityQueue(优先队列),虽然在项目里用的不多,但是它本身的设计实现还是很值得大家看一看的. ...
- Java8 LinkedHashMap 源码阅读
如果你对 HashMap 的源码有了解的话,只需要一图就能知道 LinkedHashMap 的原理了,但是具体的实现细节还是需要去读一下源码. 一.LinkedHashMap 简介 1.1 继承结构 ...
- 【java8新特性】——Stream API详解(二)
一.简介 java8新添加了一个特性:流Stream.Stream让开发者能够以一种声明的方式处理数据源(集合.数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation ...
- 放大招了,肝了一篇8万字的Java8新特性总结,赶快收藏
大家好,我是冰河~~ 说实话,肝这篇文章花了我一个月的时间,关于Java8的新特性全在这儿了,建议先收藏后阅读. Java8有哪些新特性? 简单来说,Java8新特性如下所示: Lambda表达式 函 ...
- java8 新特性精心整理(全)——新 Date/Time API
https://blog.csdn.net/sanri1993/article/details/101176712 前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自 ...
- 【小家java】java8新特性之---Optional的使用,避免空指针,代替三目运算符
相关阅读 [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小 ...
- 【java8新特性】——lambda表达式与函数式接口详解(一)
一.简介 java8于2014年发布,相比于java7,java8新增了非常多的特性,如lambda表达式.函数式接口.方法引用.默认方法.新工具(编译工具).Stream API.Date Time ...
最新文章
- PHPRPC for PHP
- 字符流读取,乱码问题
- 查看redis aof内存_Redis持久化问题定位与优化技巧
- 数据库每日一题 2020.05.09
- Eclipse用法和技巧九:自动添加try/catch块2
- Codeforces Round #162 (Div. 2): D. Good Sequences(DP)
- 求解偏微分方程开源有限元软件deal.II学习--Step 2
- linux下搭建redis内网端口映射工具-rinetd
- 新中大财务软件-A3中怎样更改IP地址
- pdf不预览直接打印(笔记记录)
- 【蓝桥杯冲刺 day12】题目全解析
- 翻译 RFC 7322: RFC 样式指南
- 使用超级表格快速创建在线表单(如问卷调查、报名表)
- 相似剩余金额宝数值添加的动画
- 重装战姬服务器维护,重装战姬2020年9月10日更新维护公告重装战姬2020年9月10日更新了什么 - 量产资源网...
- DEM影像下载、拼接、裁剪(ArcGIS)
- 南大袁春风计算机系统基础(一)笔记
- Go开发之如何破解安装GoLand编译器?
- esxi虚拟机的显卡怎么来的_关于ESXI显卡直通(VmDirectPath),使虚拟机变成HTPC的若干经验...
- android App 集成 facebook 第三方认证登录