java8学习之Collectors工厂类源码分析与实战

摘要:
让我们看看mapping()函数的方法定义:让我们看看它的具体实现:除了第二个参数,所有其他参数都使用下游的相应方法,如下所示:焦点固定在第二个:元素累加器,这也是mapping(()实现的核心。事实上,它使用下游收集器进行一些转换。下面是如何实现转换目的的详细分析:因此javadoc对应于这句话:Collectors CollectionAndThen():让我们先看看它的javadoc。接下来,让我们来看看它的具体实现:也就是说,整个判断语句删除了“IDENTITY_FISH”的特性。为什么要删除此功能?

如上一节【http://www.cnblogs.com/webor2006/p/8360232.html】在结尾处谈到的,彻底理解了Collector收集器之后,有必要对其系统Collectors实现的各种非常常见的收集器进行仔细阅读,所以这也是接下来要去完成的,下面开始。

对于Collectors类而言,它是一个工厂类,因为它的构造是一个私的有,如下:

java8学习之Collectors工厂类源码分析与实战第1张

而对于这个类中有一个静态类去实现Collector接口,这个也已经在之前详细分析过了,如下:

java8学习之Collectors工厂类源码分析与实战第2张

而对于它里面的各种实现基本可以分为两种情况:

1、通过CollectorImpl来实现,如:

java8学习之Collectors工厂类源码分析与实战第3张

2、通过reducing方法来实现,而reducting方法本身又是通过CollectorImpl来实现的,也就是说CollectorImpl是核心,如:

java8学习之Collectors工厂类源码分析与实战第4张

下面则开始一个个具体方法分析:

Collectors.toList():

这个是咱们使用最多的一个方法啦,其实现也比较容易搞懂(前提是自己对收集器原理理解得非常透彻了),下面看一下:

java8学习之Collectors工厂类源码分析与实战第5张

而其实上面的这种强制转换的方式还可以写成它:ArrayList<T>::new。

接着第二个参数是“BiConsumer<A, T> accumulator”累加器,而该方法的实现如下:

java8学习之Collectors工厂类源码分析与实战第6张

而第三个参数是“BinaryOperator<A> combiner”,将多个结果容器合并成一个,实现如下:

java8学习之Collectors工厂类源码分析与实战第7张

最后一个参数为:“Set<Characteristics> characteristics”,也就是收集器的特性,这里实现是直接传的一个常量,如下:

java8学习之Collectors工厂类源码分析与实战第8张

java8学习之Collectors工厂类源码分析与实战第9张

Collectors.toCollection():

对于toList()中间结果类型生成的就是ArrayList类型,如下:

java8学习之Collectors工厂类源码分析与实战第10张

所以方法不需要任何参数,而想要换成其它的结果容器类型那么这个方法就派上用场啦,它是一个更加通用的方法, 当然具体的容器类型可以由咱们调用方来指定了,如下:

java8学习之Collectors工厂类源码分析与实战第11张

所以此时对于收集器的第一个参数直接就传的是咱们进来的这个参数了:

java8学习之Collectors工厂类源码分析与实战第12张

其它的参数其本上跟toList()实现类似,所以就不啰嗦啦,真的是如果知道了如何自己来自定义一个收集器,对于这些系统的实现看着是非常之亲切的。

Collectors.toSet():

基本上跟toList()的实现类似,第一个参数实例化结果容器,这里传的是HashSet,如下:

java8学习之Collectors工厂类源码分析与实战第13张

第二个参数如下:

java8学习之Collectors工厂类源码分析与实战第14张

第三个参数跟上面的方法实现一样,不多解释了,最后看一下第四个特性参数,这次的跟上面两个方法不一样了,下面看下:

java8学习之Collectors工厂类源码分析与实战第15张

java8学习之Collectors工厂类源码分析与实战第16张

Collectors.joining():

该方法的作用是将集合中的元素进行字符串连接,之前也用它做过一些例子,下面看下它的具体实现:

首先看一下传的泛型类型:

java8学习之Collectors工厂类源码分析与实战第17张

可以看到中间结果类型和最终结果类型是不一样的,所以可以看到它实际上是调用CollectorImpl类带有五个参数的构造函数,如下:

java8学习之Collectors工厂类源码分析与实战第18张

相比上面的方法,多了一个finisher参数,为什么?因为中间结果类型跟最终结果类型不是同一个类型,所以需要通过调用finisher()方法对其进行类型转换嘛,好了,下面具体看一下参数是如何生成的:

java8学习之Collectors工厂类源码分析与实战第19张

java8学习之Collectors工厂类源码分析与实战第20张

java8学习之Collectors工厂类源码分析与实战第21张

java8学习之Collectors工厂类源码分析与实战第22张

最后参数这次的特性又不同了,分析下:

java8学习之Collectors工厂类源码分析与实战第23张

java8学习之Collectors工厂类源码分析与实战第24张

 接下来joining()还有以下两个方法重载,其中第二个joining()是调用最后一个joining方法来实现的,如下:

java8学习之Collectors工厂类源码分析与实战第25张

所以将注意力就集中到最后一个joining()方法之上喽:

第一个参数实始化一个中间结果容器,这里采用了一个在Java1.8中提供的一个辅助类StringJoiner,它主要是用于字符串拼接的,如下:

java8学习之Collectors工厂类源码分析与实战第26张

java8学习之Collectors工厂类源码分析与实战第27张

第二个参数为累加器,如下:

java8学习之Collectors工厂类源码分析与实战第28张

java8学习之Collectors工厂类源码分析与实战第29张

第三个参数为中间结果合并:

java8学习之Collectors工厂类源码分析与实战第30张

java8学习之Collectors工厂类源码分析与实战第31张

第四个参数则是生成结果,如下:

java8学习之Collectors工厂类源码分析与实战第32张

最后一个特性也是无特性:

java8学习之Collectors工厂类源码分析与实战第33张

Collectors.mapping():

下面先来看一下该方法的javadoc对它的说明:

java8学习之Collectors工厂类源码分析与实战第34张

java8学习之Collectors工厂类源码分析与实战第35张

那啥叫"downstream"呢?可以瞅一下mapping()函数的方法定义:

java8学习之Collectors工厂类源码分析与实战第36张

java8学习之Collectors工厂类源码分析与实战第37张

下面来看一下它的具体实现:除了第二个参数不一样之外,其它参数都是使用的downstream的相应的方法,如下:

java8学习之Collectors工厂类源码分析与实战第38张

而焦点就定格到了第二个参数:元素累加器,也是mapping()实现的核心,其实也是用的下流的收集器做了一定的转换,下面具体来分析一下是如何达到转换的目的的:

java8学习之Collectors工厂类源码分析与实战第39张

java8学习之Collectors工厂类源码分析与实战第40张

所以在javadoc上对应于这句话:

java8学习之Collectors工厂类源码分析与实战第41张

Collectors.collectingAndThen():

先看一下它的javadoc:

java8学习之Collectors工厂类源码分析与实战第42张

接下来看一下它的具体实现:

java8学习之Collectors工厂类源码分析与实战第43张

 java8学习之Collectors工厂类源码分析与实战第44张

java8学习之Collectors工厂类源码分析与实战第45张

也就是这个整个判断语句就是将"IDENTITY_FINISH"的特性去掉了,那为啥要去掉这个特性呢?其实很容易想到,因为该函数是必须要执行finisher()方法的,而如果设置有"IDENTITY_FINISH"特性则不会执行这个finisher()方法了。所以再看下面的返回:

java8学习之Collectors工厂类源码分析与实战第46张

而andThen()就是Function里面提供的方法,这个咱们之前也已经学习过了,如下:

java8学习之Collectors工厂类源码分析与实战第47张

Collectors.counting():

java8学习之Collectors工厂类源码分析与实战第48张

而这里粗略的看一下reducing()方法的参数:

java8学习之Collectors工厂类源码分析与实战第49张

根据传给reducing()方法的值,第一个参数为初始值为0;第二个参数则是将无素都转换成1,第三个则是进行元素的累加,因为所有元素值都变为1了,那总和既为元素的总个数。关于reducing()的具体实现分析放到下一次,这里有个初步印象既可。

Collectors.minBy()&Collectors.maxBy():

java8学习之Collectors工厂类源码分析与实战第50张

Collectors.summingInt():

这个收集器是用来对元素进行求和用的,下面来看一下它的具体实现:

java8学习之Collectors工厂类源码分析与实战第51张

java8学习之Collectors工厂类源码分析与实战第52张

java8学习之Collectors工厂类源码分析与实战第53张

实现是比较容易读懂的,但是有个疑问:为啥要生成一个长度为1的数组呢?最终不就是需要一个int的结果,直接生成一个int类型不就可以了,因为第一个参数是需要一个可变的结果容器,而单纯一个数字当然不是一个容器啦,另外数字是没法传递的,因为它是不可变的,所以这就是为啥要生成一个长度为1的数组的原因了。

类似的还有summingLong()、summingDouble(),这里就不再多说了。

Collectors.averagingInt():

这个是用来求平均值的,而平均值很显然是用总数除以个数,所以看一下它的实现:

java8学习之Collectors工厂类源码分析与实战第54张

java8学习之Collectors工厂类源码分析与实战第55张

java8学习之Collectors工厂类源码分析与实战第56张

java8学习之Collectors工厂类源码分析与实战第57张

其中类似的还有averagingLong()、averagingDouble(),这里也不多说了。

免责声明:文章转载自《java8学习之Collectors工厂类源码分析与实战》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇双轴按键摇杆控制器控制TFTLCD(使用ADC1双通道DMA传输)磁盘DMA过程分析下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Javaweb容器的四种作用域

几乎所有web应用容器都提供了四种类似Map的结构:application session request page,Jsp或者Servlet通过向着这四个对象放入数据,从而实现Jsp和Servlet之间数据的共享。 application:整个应用  对应servlet中ServletContext session:会话    对应servlet中Htt...

11.Vue技术栈开发实战-从SplitPane组件谈Vue中如何“操作”DOM

vue中是数据驱动视图。   创建组件的文件夹叫做split-pane 在路由列表里面注册一下 添加这个vue页面 创建组件的vue文件,设置name为SplitPane 创建index.js.把组件引进来,并导出去 引入这个组件,然后注册这个组件。 容器中分成两个区域,通过拖动来改变两个区域的比例。 还可以上下,嵌套使用。 分为三个部分,左边的区域,...

docker使用问题汇总

1.解决镜像字符集问题 在dockerfile中加入以下配置 RUN yum -y install kde-l10n-Chinese && yum -y reinstall glibc-common ENV LC_ALL zh_CN.UTF-8 ENV LANG zh_CN.UTF-8 宿主机中所有字符集安装包都在/usr/share/i1...

Rancher 2.1平台搭建及使用

一、概述 1.1、什么是Rancher 1.2、Rancher架构 1.2.1、Docker简述 1.2.2、Kubernetes简述 1.2.3、Rancher架构 二、相关术语 2.1、全局层 2.2、集群层 2.3、项目层 2.4、其他 (右上角登录菜单) 三、Rancher v2.1.0功能列表 3.1、K8S集群管理 3.2、多租户功能 3.3、...

Docker学习5-Dockerfile编写自己的镜像

前言 Dockerfile 可以自定义编写镜像,简单来说就类似写脚本,shell脚本应该接触不少了,就是一个执行后就完成了。 当然做好的镜像也可以传到镜像仓库,就好像github上面一样存储,一个命令就能照搬下来安装。 一、简介 相信不少朋友会问,为何docker run 一下python就停止了呢?(那执行完了不停止干什么?) 还有为什么我的pip安装,...

如何使用 Secret?- 每天5分钟玩转 Docker 容器技术(108)

我们经常要向容器传递敏感信息,最常见的莫过于密码了。比如: docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql 在启动 MySQL 容器时我们通过环境变量 MYSQL_ROOT_PASSWORD 设置了 MySQL 的管理员密码。不过密码是以明文的形式写在 docker run 命令中,有潜在的安...