JDK1.8新特性(六):Stream终极操作,轻松解决集合分组等复杂操作
bigegpt 2024-10-12 06:54 7 浏览
前期回顾:
JDK1.8新特性(五):Stream,集合操作利器,让你好用到飞起来
上一篇JDK1.8新特性(五):Stream,集合操作利器,让你好用到飞起来,主要讲解了关于Stream的基本操作,可以轻松摆脱"遍历、再遍历、再运算"等复杂操作,但Stream远远不止这些。本文将讲述关于Stream的终极操作,让你轻松解决集合的分组、汇总等操作,让其他同事对你刮目相看。
一、Collectors
java.util.stream.Collectors,是从JDK1.8开始新引入的一个类。从源码的类注释上,我们可以知道:Collectors实现了各种有用归约的操作,例如类型归类到新集合、根据不同标准汇总元素等。透过示例,能让我们眼前一亮,短短的一行代码却能处理如此强大、复杂的功能:汇总、拼接、累加计算、分组等。
切记,不要用错哦,是java.util.stream.Collectors,不是java.util.Collections。
/**
* Implementations of {@link Collector} that implement various useful reduction
* operations, such as accumulating elements into collections, summarizing
* elements according to various criteria, etc.
*
* <p>The following are examples of using the predefined collectors to perform
* common mutable reduction tasks:
*
* <pre>{@code
* // Accumulate names into a List
* List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
*
* // Accumulate names into a TreeSet
* Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
*
* // Convert elements to strings and concatenate them, separated by commas
* String joined = things.stream()
* .map(Object::toString)
* .collect(Collectors.joining(", "));
*
* // Compute sum of salaries of employee
* int total = employees.stream()
* .collect(Collectors.summingInt(Employee::getSalary)));
*
* // Group employees by department
* Map<Department, List<Employee>> byDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment));
*
* // Compute sum of salaries by department
* Map<Department, Integer> totalByDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment,
* Collectors.summingInt(Employee::getSalary)));
*
* // Partition students into passing and failing
* Map<Boolean, List<Student>> passingFailing =
* students.stream()
* .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
*
* }</pre>
*
* @since 1.8
*/
换句话说,Collectors结合Stream将成为集合的终极操作,其中,包括:
- 类型归类:将集合中元素按照类型、条件过滤等归类,存放到指定类型的新集合。
- 分组:按照条件对元素进行分组,和SQL中group by的用法有异曲同工之妙。
- 分区:分组的特殊情况,实质是在做二分组,将符合条件、不符合条件的元素分组到两个key分别为true和false的Map中,从而我们能够得到符合和不符合的分组新集合。
- 最值:按照某个属性查找最大、最小元素。
- 累加、汇总:用来完成累加计算、数据汇总(总数、总和、最小值、最大值、平均值)。
- 连接:将元素以某种规则连接起来。
- ……
二、实战演练
1. 类型归类
将集合中元素按照类型、条件过滤等归类,存放到指定类型的新集合,List、Map、Set、Collection或者ConcurrentMap。涉及以下方法:
- Collectors.toList()
- Collectors.toMap()
- Collectors.toSet()
- Collectors.toCollection()
- Collectors.toConcurrentMap()
一般都作为终止操作符cololect的参数来使用,并伴随着流的结束。
常用于收集、筛选出集合(复杂集合)中的符合条件的数据,并存放于对应类型的新集合中,便于后续实际业务逻辑处理。
比如,将名字类型归类存在到List<String>集合中:
List<String> list = allPeoples.stream().map(People::getName).collect(Collectors.toList());
2. 分组
按照条件对元素进行分组,和 SQL 中的 group by 用法有异曲同工之妙,通常也建议使用Java代码进行分组处理以减轻数据库SQL压力。
分组涉及以下方法:
- Collectors.groupingBy(…):普通分组。
- Collectors.groupingByConcurrent(…):线程安全的分组。
分组后,返回的是一个Map集合,其中key作为分组对象,value作为对应分组结果。
比如,考虑到People集合中可能会存在同龄人,将集合按照年龄进行分组:
Map<Integer, List<People>> groupingByAge = allPeoples.stream().collect(Collectors.groupingBy(People::getAge));
如果我们不想返回Map的value为List怎么办?实际上可以按照下面的方式分组:
Map<Integer, Set<People>> groupingByAge2 = allPeoples.stream().collect(Collectors.groupingBy(People::getAge, Collectors.toSet()));
考虑到同步安全问题时,怎么办?
采用线程安全的分组Collectors.groupingByConcurrent(…),于是:
Map<Integer, List<People>> groupingByAge3 = allPeoples.stream().collect(Collectors.groupingByConcurrent(People::getAge));
3. 分区
是分组的特殊情况,采用Collectors.partitioningBy(…)方法来完成。
该方法实质是在做二分组,将符合条件、不符合条件的元素分组到两个key分别为true和false的Map中,从而我们能够得到符合和不符合的分组新集合。
比如,People集合中人名有中文名,也有英文名,将人名按照中、英文名进行分区:
Map<Boolean, List<People>> partitioningByName = allPeoples.stream().collect(Collectors.partitioningBy(people -> people.getName().matches("^[a-zA-Z]*")));
// 获取英文名集合
List<People> englishNames = partitioningByName.get(true);
// 获取中文名集合
List<People> chineseNames = partitioningByName.get(false);
4. 最值
按照某个属性查找出最大或最小值元素,并且基于Comparator接口来对其进行比较,返回一个Optional对象,并结合Optional.isPresent()判断并取得最大或最小值。
涉及以下方法:
- Collectors.maxBy(…):最大值。
- Collectors.minBy(…):最小值。
比如,找到People集合中最大、最小年龄的人:
// 查找最大年龄的人
Optional<People> maxAgeOptional = allPeoples.stream().collect(Collectors.maxBy(Comparator.comparingInt(People::getAge)));
People maxAgePeople = null;
if (maxAgeOptional.isPresent()) {
maxAgePeople = maxAgeOptional.get();
}
// 查找最小年龄的人
Optional<People> minAgeOptional = allPeoples.stream().collect(Collectors.minBy(Comparator.comparingInt(People::getAge)));
People minAgePeople = null;
if (minAgeOptional.isPresent()) {
minAgePeople = minAgeOptional.get();
}
5. 累加、汇总
用来完成累加计算、数据汇总(总数、总和、最小值、最大值、平均值)操作。
计算集合某个属性的总和,类似于SQL中的sum函数。
涉及以下方法:
- Collectors.summingInt/Double/Long(…):按照某个属性求和。
- Collectors.summarizingInt/Double/Long(…):按照某个属性的数据进行汇总,得到其总数、总和、最小值、最大值、平均值。
比如,计算全体人员的薪资总和:
int salaryTotal = allPeoples.stream().collect(Collectors.summingInt(People::getSalary));
如果想要得到全体人员的薪资数据整体情况(包括总数、总和、最小值、最大值、平均值),怎么办呢?
难道分别要搞多个Stream流吗?
当然,没有这么麻烦,只需Collectors.summarizingInt方法就可轻松搞定。
// 输出:IntSummaryStatistics{count=10, sum=45000, min=2000, average=4500.000000, max=7000}
IntSummaryStatistics intSummaryStatistics = allPeoples.stream().collect(Collectors.summarizingInt(People::getSalary));
6. 连接
将元素以某种规则连接起来,得到一个连接字符串。
涉及以下方法:
- Collectors.joining():字符串直接连接。
- Collectors.joining(CharSequence delimiter):按照字符delimiter进行字符串连接。
- Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix):按照前缀prefix,后缀suffix,并以字符delimiter进行字符串连接。
比如,将People集合中所有名字按照某种连接符进行字符串连接:
// 输出:xcbeyondNikiXiaoMing超哥小白小红LucyLily超级飞侠乐迪
String namesStr1 = allPeoples.stream().map(People::getName).collect(Collectors.joining());
// 输出:xcbeyond,Niki,XiaoMing,超哥,小白,小红,Lucy,Lily,超级飞侠,乐迪
String namesStr2 = allPeoples.stream().map(People::getName).collect(Collectors.joining(","));
// 输出:[xcbeyond,Niki,XiaoMing,超哥,小白,小红,Lucy,Lily,超级飞侠,乐迪]
String namesStr3 = allPeoples.stream().map(People::getName).collect(Collectors.joining(",", "[", "]"));
三、总结
本文,只是针对JDK1.8java.util.stream.Collectors中最好用的操作进行单独举例说明,不涉及嵌套、复合、叠加使用,实际业务场景下可能会涉及到多种操作的叠加、组合使用,需按需灵活使用即可。
如果你熟悉了上面这些操作,在面对复杂集合、处理复杂逻辑时,就会更加得心应手。尤其是分组、汇总,简直是太好用了。
在JDK1.8的使用过程中,你还遇到哪些好用、好玩的终极操作呢?
相关推荐
- Linux gron 命令使用详解(linux gminer)
-
简介gron是一个独特的命令行工具,用于将JSON数据转换为离散的、易于grep处理的赋值语句格式。它的名字来源于"grepableon"或"grepable...
- 【Linux】——从0到1的学习,让你熟练掌握,带你玩转Linu
-
学习Linux并掌握Java环境配置及SpringBoot项目部署是一个系统化的过程,以下是从零开始的详细指南,帮助你逐步掌握这些技能。一、Linux基础入门1.安装Linux系统选择发行版:推荐...
- Linux常用的shell命令汇总(linux中shell的作用)
-
本文介绍Linux系统下常用的系统级命令,包括软硬件查看、修改命令,有CPU、内存、硬盘、网络、系统管理等命令。说明命令是在Centos6.464位的虚拟机系统进行测试的。本文介绍的命令都会在此C...
- 零成本搭建个人加密文件保险柜(适用于 Win11 和 Linux)
-
不依赖收费软件操作简单,小白也能跟着做支持双系统,跨平台使用实现数据加密、防删除、防泄露内容通俗无技术门槛,秒懂秒用使用工具简介我们将使用两个核心工具:工具名用途系统支持Veracrypt创建加密虚...
- 如何在 Linux 中使用 Gzip 命令?(linux怎么用gzip命令)
-
gzip(GNUzip)是Linux系统中一个开源的压缩工具,用于压缩和解压缩文件。它基于DEFLATE算法,广泛应用于文件压缩、备份和数据传输。gzip生成的文件通常带有.gz后缀,压缩效率...
- Linux 必备的20个核心知识点(linux内核知识点)
-
学习和使用Linux所必备的20个核心知识点。这些知识点涵盖了从基础操作到系统管理和网络概念,是构建扎实Linux技能的基础。Linux必备的20个知识点1.Linux文件系统层级标...
- 谷歌 ChromeOS 已支持 7z、iso、tar 文件格式
-
IT之家6月21日消息,谷歌ChromeOS在管理文件方面进行了改进,新增了对7z、iso和tar等格式的支持。从5月的ChromeOS101更新开始,ChromeOS...
- 如何在 Linux 中提取 Tar Bz2 文件?
-
在深入解压方法之前,我们先来了解.tar.bz2文件的本质。.tar.bz2是一种组合文件格式,包含两个步骤:Tar(TapeArchive):tar是一种归档工具,用于将多个文件或目录打包...
- 如何在 CentOS 7/8 上安装 Kitematic Docker 管理器
-
Kitematic是一款流行的Docker图形界面管理平台,适用于Ubuntu、macOS和Windows操作系统。然而,其他发行版(如CentOS、OpenSUSE、Fedora、R...
- Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!
-
之前和大家分享过JDK17的多版本管理及详细安装过程,然后在项目升级完jdk17后又发现之前的注册和配置中心nacos又用不了,原因是之前的nacos1.3版本的,版本太老了,已经无法适配当前新的JD...
- 爬虫搞崩网站后,程序员自制“Zip炸弹”反击,6刀服务器成功扛住4.6万请求
-
在这个爬虫横行的时代,越来越多开发者深受其害:有人怒斥OpenAI的爬虫疯狂“偷”数据,7人团队十年心血的网站一夜崩溃;也有人被爬虫逼到极限,最后只好封掉整个巴西的访问才勉强止血。但本文作者却走...
- Ubuntu 操作系统常用命令详解(ubuntu必学的60个命令)
-
UbuntuLinux是一款流行的开源操作系统,广泛应用于服务器、开发、学习等场景。命令行是Ubuntu的灵魂,也是高效、稳定管理系统的利器。本文按照各大常用领域,详细总结Ubuntu必学...
- Linux面板8.0.54 测试版-已上线(linux主机面板)
-
Linux面板8.0.54测试版【增加】[网站]Java项目新增刷新列表按钮【增加】[网站]PHP项目-Apache-服务新增守护进程功能【增加】[网站]Python项目创建/删除网站时新增同时创建...
- 开源三剑客——构建私有云世界的基石
-
公共云原生的浪潮正在席卷这个世界,亚马逊AWS、谷歌GCP和微软的Azure年收入增长超过了30%,越来越多的公司和个人开始将自己的服务部署到云环境中,大型数据中心的规模经济带来了成本的降低,可以在保...
- 2.2k star,一款业界领先的私有云+在线文档管理系统
-
简介kodbox可道云(原KodExplorer)是业内领先的企业私有云和在线文档管理系统,为个人网站、企业私有云部署、网络存储、在线文档管理、在线办公等提供安全可控,简便易用、可高度定制的私有云产品...
- 一周热门
- 最近发表
-
- Linux gron 命令使用详解(linux gminer)
- 【Linux】——从0到1的学习,让你熟练掌握,带你玩转Linu
- Linux常用的shell命令汇总(linux中shell的作用)
- 零成本搭建个人加密文件保险柜(适用于 Win11 和 Linux)
- 如何在 Linux 中使用 Gzip 命令?(linux怎么用gzip命令)
- Linux 必备的20个核心知识点(linux内核知识点)
- 谷歌 ChromeOS 已支持 7z、iso、tar 文件格式
- 如何在 Linux 中提取 Tar Bz2 文件?
- 如何在 CentOS 7/8 上安装 Kitematic Docker 管理器
- Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- linux安装minio (74)
- ubuntuunzip (67)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)