Java函数式编程Stream.collect()为什么这么受欢迎?

Wesley13
• 阅读 455

前几天更新的文章内容相信前面繁琐的内容已彻底打消了你学习Java函数式编程的热情,不过很遗憾,下面的内容更繁琐。但这不能怪Stream类库,因为要实现的功能本身很复杂。

Java函数式编程Stream.collect()为什么这么受欢迎?

收集器(Collector)是为Stream.collect()方法量身打造的工具接口(类)。考虑一下将一个Stream转换成一个容器(或者Map)需要做哪些工作?我们至少需要两样东西:

  • 目标容器是什么?是ArrayList还是HashSet,或者是个TreeMap。

  • 新元素如何添加到容器中?是List.add()还是Map.put()。如果并行的进行规约,还需要告诉collect()

  • 多个部分结果如何合并成一个。

结合以上分析,collect()方法定义为 R collect(Supplier supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner),三个参数依次对应上述三条分析。不过每次调用collect()都要传入这三个参数太麻烦,收集器Collector就是对这三个参数的简单封装,所以collect()的另一定义为<R,A> R collect(Collector<? super T,A,R> collector)。Collectors工具类可通过静态方法生成各种常用的Collector。举例来说,如果要将Stream规约成List可以通过如下两种方式实现:

Java函数式编程Stream.collect()为什么这么受欢迎?

通常情况下我们不需要手动指定collect()的三个参数,而是调用collect(Collector<? super T,A,R> collector)方法,并且参数中的Collector对象大都是直接通过Collectors工具类获得。实际上传入的收集器的行为决定了collect()的行为。

使用collect()生成Collection

前面已经提到通过collect()方法将_Stream_转换成容器的方法,这里再汇总一下。将_Stream_转换成_List_或_Set_是比较常见的操作,所以Collectors工具已经为我们提供了对应的收集器,通过如下代码即可完成:

Java函数式编程Stream.collect()为什么这么受欢迎?

上述代码能够满足大部分需求,但由于返回结果是接口类型,我们并不知道类库实际选择的容器类型是什么,有时候我们可能会想要人为指定容器的实际类型,这个需求可通过Collectors.toCollection(Supplier collectionFactory)方法完成。

Java函数式编程Stream.collect()为什么这么受欢迎?

上述代码(3)处指定规约结果是_ArrayList_,而(4)处指定规约结果为_HashSet_。一切如你所愿。

使用collect()生成Map

前面已经说过Stream背后依赖于某种数据源,数据源可以是数组、容器等,但不能是Map。反过来从Stream生成Map是可以的,但我们要想清楚Map的key和value分别代表什么,根本原因是我们要想清楚要干什么。通常在三种情况下collect()的结果会是Map:

  • 使用Collectors.toMap()生成的收集器,用户需要指定如何生成Map的key和value。

  • 使用Collectors.partitioningBy()生成的收集器,对元素进行二分区操作时用到。

  • 使用Collectors.groupingBy()生成的收集器,对元素做group操作时用到。

情况1:使用toMap()生成的收集器,这种情况是最直接的,前面例子中已提到,这是和Collectors.toCollection()并列的方法。如下代码展示将学生列表转换成由<学生,GPA>组成的Map。非常直观,无需多言。

Java函数式编程Stream.collect()为什么这么受欢迎?

情况2:使用partitioningBy()生成的收集器,这种情况适用于将Stream中的元素依据某个二值逻辑(满足条件,或不满足)分成互补相交的两部分,比如男女性别、成绩及格与否等。下列代码展示将学生分成成绩及格或不及格的两部分。

Java函数式编程Stream.collect()为什么这么受欢迎?

情况3:使用groupingBy()生成的收集器,这是比较灵活的一种情况。跟SQL中的_group by_语句类似,这里的_groupingBy()_也是按照某个属性对数据进行分组,属性相同的元素会被对应到_Map_的同一个_key_上。下列代码展示将员工按照部门进行分组:

Java函数式编程Stream.collect()为什么这么受欢迎?

以上只是分组的最基本用法,有些时候仅仅分组是不够的。在SQL中使用_group by_是为了协助其他查询,比如

  • 先将员工按照部门分组

  • 然后统计每个部门员工的人数。

Java类库设计者也考虑到了这种情况,增强版的groupingBy()能够满足这种需求。增强版的groupingBy()允许我们对元素分组之后再执行某种运算,比如求和、计数、平均值、类型转换等。这种先将元素分组的收集器叫做上游收集器,之后执行其他运算的收集器叫做下游收集器(downstream Collector)。

Java函数式编程Stream.collect()为什么这么受欢迎?

上面代码的逻辑是不是越看越像SQL?高度非结构化。还有更狠的,下游收集器还可以包含更下游的收集器,这绝不是为了炫技而增加的把戏,而是实际场景需要。考虑将员工按照部门分组的场景,如果我们想得到每个员工的名字(字符串),而不是一个个Employee对象,可通过如下方式做到:

Java函数式编程Stream.collect()为什么这么受欢迎?

使用collect()做字符串join

这个肯定是大家喜闻乐见的功能,字符串拼接时使用Collectors.joining()生成的收集器,从此告别_for_循环。Collectors.joining()方法有三种重写形式,分别对应三种不同的拼接方式。无需多言,代码过目难忘。

Java函数式编程Stream.collect()为什么这么受欢迎?

collect()还可以做更多

除了可以使用Collectors工具类已经封装好的收集器,我们还可以自定义收集器,或者直接调用collect(Supplier supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)方法,收集任何形式你想要的信息。不过Collectors工具类应该能满足我们的绝大部分需求,手动实现之间请先看看文档。

- END -

往期推荐

[

JDK1.8升级这么久!Stream流的规约操作有哪些?

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247499039%26idx%3D1%26sn%3D8987969f710ec22ef0cae652398b3127%26chksm%3Dfd63eda0ca1464b66d62832a3942fa043ed4376da6a2535c840eb495f5737947def401facb35%26scene%3D21%23wechat_redirect)

[

函数式编程Stream接口真的有那么好用吗?

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247498836%26idx%3D1%26sn%3D3ddb7d72d45695b0e39a9ed2e4c8e4e4%26chksm%3Dfd63ecebca1465fd487fe22c8f7590aa687859dfc463eb82d0ceadf765f425f49cadc077457c%26scene%3D21%23wechat_redirect)

[

Kafka在哪些场景下会造成重复消费或消息丢失?

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247498633%26idx%3D1%26sn%3D195a7c5bc2e29526aa01904d51e1e80a%26chksm%3Dfd63eb36ca1462200f053b71caa1f8353e9c7ecc55b138e596cc79408a46ce06ca017c989bdc%26scene%3D21%23wechat_redirect)

[

Map在Java 8中增加非常实用哪些函数接口?

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247498614%26idx%3D1%26sn%3D7846993eecf3db9b00b4718d8a050292%26chksm%3Dfd63ebc9ca1462dfe21ab41c06476bfcd2ee5d47fb91e56e7c9044f981471899232b5ce4cfbb%26scene%3D21%23wechat_redirect)

[

Kafka消费者提交消费位移时提交的是当前消费到的最新消息的 offset 还是 offset+1?

](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU3OTc1MDM1Mg%3D%3D%26mid%3D2247498614%26idx%3D2%26sn%3D9d5305e82d4ceea6e2e63aeef4698da7%26chksm%3Dfd63ebc9ca1462df178bc7261dba35ed1c54825e75ca13fd42a1605a2f21c9924c1e780b77f2%26scene%3D21%23wechat_redirect)

Java函数式编程Stream.collect()为什么这么受欢迎?

本文分享自微信公众号 - 码农架构(iByteCoding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这