JavaSE笔记——函数式编程(高级集合类和收集器)

news2024/11/29 20:34:42

文章目录

  • 前言
  • 一、方法引用
  • 二、元素顺序
  • 三、使用收集器
    • 1.转换成其他集合
    • 2.转换成值
    • 3.数据分块
    • 4.数据分组
    • 5.字符串
    • 6.组合收集器
  • 总结


前言

前面介绍了集合类的部分变化,事实上,Java 8 对集合类的改进不止这些。现在是时候介绍一些高级主题了,包括新引入的 Collector 类。同时介绍方法引用,它可以帮助大家在 Lambda 表达式中轻松使用已有代码。编写大量使用集合类的代码时,使用方法引用能让程序员获得丰厚的回报。


一、方法引用

Lambda 表达式有一个常见的用法:Lambda 表达式经常调用参数。比如想得到艺术家的姓名,Lambda 的表达式如下:

artist -> artist.getName()

这种用法如此普遍,因此 Java 8 为其提供了一个简写语法,叫作方法引用,帮助程序员重用已有方法。用方法引用重写上面的 Lambda 表达式,代码如下:

Artist::getName

标准语法为 Classname::methodName。需要注意的是,虽然这是一个方法,但不需要在后面加括号,因为这里并不调用该方法。我们只是提供了和 Lambda 表达式等价的一种结构,在需要时才会调用。凡是使用 Lambda 表达式的地方,就可以使用方法引用。

构造函数也有同样的缩写形式,如果你想使用 Lambda 表达式创建一个 Artist 对象,可能会写出如下代码:

(name, nationality) -> new Artist(name, nationality)

使用方法引用,上述代码可写为:

Artist::new

这段代码不仅比原来的代码短,而且更易阅读。Artist::new 立刻告诉程序员这是在创建一个 Artist 对象,程序员无需看完整行代码就能弄明白代码的意图。另一个要注意的地方是方法引用自动支持多个参数,前提是选对了正确的函数接口。

二、元素顺序

另外一个尚未提及的关于集合类的内容是流中的元素以何种顺序排列。我们可能知道,一些集合类型中的元素是按顺序排列的,比如 List;而另一些则是无序的,比如 HashSet。增加了流操作后,顺序问题变得更加复杂。

直观上看,流是有序的,因为流中的元素都是按顺序处理的。这种顺序称为出现顺序。出现顺序的定义依赖于数据源和对流的操作。

在一个有序集合中创建一个流时,流中的元素就按出现顺序排列,如下:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> list = numbers.stream().collect(Collectors.toList());
System.out.println(numbers);
System.out.println(list);

在这里插入图片描述

如果集合本身就是无序的(及插入顺序和输入顺序不一致),由此生成的流也是无序的,流中的元素就会按照集合的出现顺序进行处理。

Set<Integer> integerSet = new HashSet<>();
integerSet.add(1);
integerSet.add(22);
integerSet.add(500);
System.out.println(integerSet);
integerSet.stream().forEach(integer -> System.out.print(integer + ","));

在这里插入图片描述

流的目的不仅是在集合类之间做转换,而且同时提供了一组处理数据的通用操作。有些集合本身是无序的,但有些操作有时会产生顺序:

Set<Integer> integerSet = new HashSet<>();
integerSet.add(6);
integerSet.add(3);
integerSet.add(5);
List<Integer> sameOrder = integerSet.stream().sorted().collect(Collectors.toList());
System.out.println(sameOrder);

在这里插入图片描述

三、使用收集器

前面使用过 collect(Collectors.toList()),在流中生成列表。显然,List 是能想到的从流中生成的最自然的数据结构,但是有时人们还希望从流生成其他值,比如 Map 或 Set,或者你希望定制一个类将你想要的东西抽象出来。

前面已经讲过,仅凭流上方法的签名,就能判断出这是否是一个及早求值的操作。reduce 操作就是一个很好的例子,但有时人们希望能做得更多。

这就是收集器,一种通用的、从流生成复杂值的结构。只要将它传给 collect 方法,所有的流就都可以使用它了。

1.转换成其他集合

有一些收集器可以生成其他集合。比如前面已经见过的 Collectors.toList(),生成了 java.util.List 类的实例。到目前为止,了解了很多流上的链式操作,但总有一些时候,需要最终生成一个集合——比如:

  1. 已有代码是为集合编写的,因此需要将流转换成集合传入;
  2. 在集合上进行一系列链式操作后,最终希望生成一个值;
  3. 写单元测试时,需要对某个具体的集合做断言。

通常情况下,创建集合时需要调用适当的构造函数指明集合的具体类型:

List<Artist> artists = new ArrayList<>();

但是调用 toList 或者 toSet 方法时,不需要指定具体的类型。Stream 类库在背后自动为你挑选出了合适的类型。

可能还会有这样的情况,你希望使用一个特定的集合收集值,而且你可以稍后指定该集合的类型。比如,你可能希望使用 TreeSet,而不是由框架在背后自动为你指定一种类型的Set。此时就可以使用toCollection,它接受一个函数作为参数,来创建集合。

stream.collect(Collectors.toCollection(TreeSet::new));

2.转换成值

还可以利用收集器让流生成一个值。maxBy 和 minBy 允许用户按某种特定的顺序生成一个值。

public Optional<Artist> biggestGroup(Stream<Artist> artists) {
    Function<Artist,Long> getCount = artist -> artist.getArtists().stream().count();
    return artists.max(Comparator.comparing(getCount));
}

minBy 就如它的方法名,是用来找出最小值的。

3.数据分块

另外一个常用的流操作是将其分解成两个集合。。假设有一个艺术家组成的流,你可能希望将其分成两个部分,一部分是独唱歌手,另一部分是由多人组成的乐队。可以使用两次过滤操作,分别过滤出上述两种艺术家。

但是这样操作起来有问题。首先,为了执行两次过滤操作,需要有两个流。其次,如果过滤操作复杂,每个流上都要执行这样的操作,代码也会变得冗余。

有这样一个收集器 partitioningBy,它接受一个流,并将其分成两部分。它使用 Predicate 对象判断一个元素应该属于哪个部分,并根据布尔值返回一个 Map 到列表。因此,对于 true List 中的元素,Predicate 返回 true;对其他 List 中的元素,Predicate 返回 false。

在这里插入图片描述

使用它,我们就可以将乐队(有多个成员)和独唱歌手分开了。

public Map<Boolean, List<Artist>> bandsAndSolo(Stream<Artist> artists) {
    return artists.collect(Collectors.partitioningBy(Artist::isSolo));
}

4.数据分组

数据分组是一种更自然的分割数据操作,与将数据分成 ture 和 false 两部分不同,可以使用任意值对数据分组。比如现在有一个由专辑组成的流,可以按专辑当中的主唱对专辑分组。

public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
    return albums.collect(Collectors.groupingBy(album -> album.getMainMusician()));
}

和其他一样,调用流的 collect 方法,传入一个收集器。groupingBy 收集器接受一个分类函数,用来对数据分组,就像 partitioningBy 一样,接受一个 Predicate 对象将数据分成 ture 和 false 两部分。我们使用的分类器是一个 Function 对象,和 map 操作用到的一样。

在这里插入图片描述
我们知道SQL 中的 group by 操作,groupingBy方法是和这类似的一个概念,只不过在 Stream 类库中实现了而已

5.字符串

很多时候,收集流中的数据都是为了在最后生成一个字符串。假设我们想将参与制作一张专辑的所有艺术家的名字输出为一个格式化好的列表。

Java 8 还未发布前,实现该功能的代码可能通过不断迭代列表,使用一个 StringBuilder 对象来记录结果。每一步都取出一个艺术家的名字,追加到 StringBuilder对象。使用 Java 8 提供的流和收集器就能写出更清晰的代码:

String result = artists.stream().map(Artist::getName).collect(Collectors.joining(", ", "[", "]"));

这里使用 map 操作提取出艺术家的姓名,然后使用 Collectors.joining 收集流中的值,该方法可以方便地从一个流得到一个字符串,允许用户提供分隔符(用以分隔元素)、前缀和后缀。

6.组合收集器

前面看到的各种收集器已经很强大了,但如果将它们组合起来,会变得更强大。之前我们使用主唱将专辑分组,现在来考虑如何计算一个艺术家的专辑数量。

public Map<Artist, Long> numberOfAlbums(Stream<Album> albums) {
    return albums.collect(Collectors.groupingBy(album -> album.getMainMusician(), Collectors.counting()));
}

groupingBy 先将元素分成块,每块都与分类函数 getMainMusician 提供的键值相关联,然后使用下游的另一个收集器收集每块中的元素,最后将结果映射为一个 Map。


总结

方法引用是一种引用方法的轻量级语法,形如:ClassName::methodName,收集器可用来计算流的最终值,是 reduce 方法的模拟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LInkedList的模拟实现

在之前的文章笔者介绍了链表的实现&#xff1a;无头单向非循环链表的实现&#xff01;感兴趣的各位老铁可以点进来看看&#xff1a;https://blog.csdn.net/weixin_64308540/article/details/128397961?spm1001.2014.3001.5502对于此篇博客&#xff0c;在一写出来&#xff0c;便…

java学习day72(乐友商城)微信支付实现

今日目标&#xff1a; 会调用订单系统接口 实现订单结算功能 实现微信支付功能 1.订单系统接口 我们不做开发&#xff0c;只讲解 1.1.导入订单服务 把课前资料提供的leyou-order复制到D:\heima\code\leyou目录。 然后在工程内导入&#xff1a; 然后导入module&#xff1a…

java:责任链设计模式配合Spring@Order注解使用场景

java&#xff1a;责任链设计模式配合SpringOrder注解使用场景 1 前言 java的责任链模式&#xff0c;经典使用的场景为SpringMVC的doDispatch下&#xff0c;针对请求的过滤链式行为。实际开发场景中&#xff0c;可配合Spring的Order注解&#xff0c;定义1个有顺序的链式Compon…

我在深圳的三次工厂旅程 (一)

2019年末加入一家人工智能AI创业公司&#xff0c;由于公司涉及到智能硬件产品&#xff0c;所以有机会参与到硬件产品的整个研发测试端、产品迭代流、工厂生产序等方面的这些事情。相对于研发测试、产品迭代这些在过往的工作中经历中相对比较熟悉和接触频繁&#xff0c;在软件产…

鉴定完毕!来看看跨年晚会谁假唱了…欧莱雅智能画眉设备;首个AI律师下月出庭;推特又裁员;GitHub今日热榜 | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f3a1; 『谁在假唱』技术手段分析跨年演唱会上的歌手们 各大卫视的跨年演唱会&#xff0c;你追了几场&#xff1f;看出来了谁在对口型&#xff1f;B站Up主…

【北京理工大学-Python 数据分析-2.2Matplotlib绘制饼图、直方图、极坐标、散点图】

pyplot的基础图标函数 函数说明plt.plot(x,y,fmt,…)绘制一个坐标图plt.boxplot(data,notch,position)绘制一个箱型图plt.bar(left,height,width,bottom)绘制一个条形图plt.barh(width,bottom,left,height)绘制一个横向条形图plt.polar(theta,r)绘制极坐标图plt.psd(x,NFFT256…

Dokcer14_5:Docker Compose volumes解析、Docker Compose volumes目录路径生成规则

Dokcer14_5&#xff1a;Docker Compose volumes解析、Docker Compose volumes目录路径生成规则docker-compose volumes语法语法格式及其三种变体1.无来源 &#xff1a;匿名挂载主机系统上的目录路径2.非路径源&#xff1a;具名挂载&#xff08;常用&#xff09;主机系统上的目录…

mysql idb,frm文件复制恢复

idb是innodb数据文件frm是innodb表结构文件在数据库的data目录下可以看到 data下的目录名就是数据库名&#xff0c;打开该数据库文件夹对于一个表有2个文件&#xff0c;一个以idb结尾&#xff0c;一个以frm结尾直接复制粘贴是不识别的&#xff0c;提示找不到该表解决方法&#…

在线实习项目|Python爬虫助力疫情数据追踪在线实习项目

项目介绍 项目背景&#xff1a;2019-NCOV新型冠状病毒引发的肺炎牵动全国人民的心&#xff0c;本项目希望通过大数据技术为抗击新冠肺炎贡献一份力量。 项目目标&#xff1a;使用PYTHON爬虫技术爬取疫情数据&#xff0c;从不同维度分析数据&#xff0c;并用 MATPLOT…

ubuntu18.04部署DXSLAM,CNN+VSLAM,CPU实时运行

一、下载源代码 打开终端&#xff0c;输入命令克隆仓库 git clone https://github.com/raulmur/DXSLAM.git DXSLAM二、配置环境 We have tested the library in Ubuntu 16.04 and Ubuntu 18.04, but it should be easy to compile in other platforms. C11 or C0x CompilerPa…

虚拟化技术学习笔记8

添加网卡&#xff1a; 1、virt-manager: 选择虚拟机直接添加网卡操作。 2、virsh命令&#xff1a; virsh list virsh domiflist centos7-1 virsh attach-interface centos7-1 \ --type network \ --source default \ --model virtio \ --config 虚拟机查看网卡的添加情况&…

分享65个NET源码,总有一款适合您

NET源码 分享65个NET源码&#xff0c;总有一款适合您 65个NET源码链接&#xff1a;https://pan.baidu.com/s/19yFm_9K_L0xfykMP1hdP5A?pwdn2p7 提取码&#xff1a;n2p7 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#x…

Apache APISIX Ingress 1.6 正式发布!

距离上一个版本 v1.5 发布&#xff0c;已经过了 3 个月&#xff0c;我们很高兴地宣布 Apache APISIX Ingress v1.6 正式发布&#xff01; 在该版本中&#xff0c;共有 29 位贡献者 参与代码提交&#xff0c;其中 17 位是新晋贡献者 &#xff0c;感谢大家的支持和参与&#xff…

nacos的服务注册与调用

此篇博客进行一个简单实例进行展示服务注册和服务的调用&#xff0c;以订单与库存模块为例&#xff0c;其结构图如下&#xff1a; 目 录 1、创建订单模块与库存模块 1.1、编写stock模块 1.2、编写order模块 2、访问地址进行测试 3、总结 1、创建订单模块与库存模块 创建两…

论文投稿指南——中文核心期刊推荐(生物科学 2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

.net开发安卓入门-文件操作与配置操作

.net开发安卓入门-文件操作与配置操作文件操作内部存储代码运行效果System.Environment.SpecialFolder枚举类型对应路径表格外部存储&#xff08;代码和效果见上图&#xff09;区别缓存SharedPreferences获取SharedPreferences对象方法列表读取配置信息写配置信息AssetsNlog配置…

【Linux项目自动化构建工具 make/Makefile】

目录 1 背景 2 原理 3 Linux第一个小程序&#xff0d;进度条 3.1 行缓冲区概念 3.2 进度条代码 4 总结 1 背景 在VS中我们知道当我们想要运行程序时直接按f5程序就会自动运行起来&#xff0c;但是在Linux中如果有多个文件好像并不能这样快速进行&#xff0c;那么这时候就…

远程接入(远程办公)解决方案 OpenText™ Exceed™ TurboX (ETX)

OpenText™ Exceed™ TurboX 安全快速的虚拟应用程序和桌面解决方案&#xff0c;适用于混合工作环境&#xff0c;只需低带宽互联网连接即可为办公桌面和图形要求苛刻的软件提供卓越的用户体验。 突出优势&#xff1a; 支持混合云环境使用任何设备随时随地远程工作为用户提供类…

SpringCloud之Zuul路由网关

Zuul路由网关1. Zuul的概念2. Zuul的作用3. 案例1. Zuul的概念 Zuul包含了对请求的路由&#xff08;用来跳转的&#xff09;和过滤两个最主要功能&#xff1a; 其中路由功能负责将外部请求转发到具体的微服务实例上&#xff0c;是实现外部访问统一入口的基础&#xff0c;而过滤…

新年新故事 | Nice 兔 Meet U

各位伙伴新年好哇 比特熊又回来啦【比特熊故事汇2.0】23年开年第一场与大家一起认识“印象中”的不寻常开发人产品创新实践、工作 Mix 生活、反差感2023年1月12日19:00-20:00【比特熊故事汇2.0】新年想见你第一面Nice 兔 Meet U 比特熊兔年特别限定手办2023年兔年来到&#xff…