JDK1.8 新特性 (六):Stream 的终极操作,轻松解决集合分组、汇
JDK1.8新特性(五):Stream,集合操作利器,让你好用到飞起来
上一篇JDK1.8新特性(五):Stream,集合操作利器,让你好用到飞起来,主要讲解了关于Stream的基本操作,可以轻松摆脱“遍历、再遍历、再运算”等复杂操作,但Stream远远不止这些。本文将讲述关于Stream的终极操作,让你轻松解决集合的分组、汇总等操作,让其他同事对你刮目相看。
一、Collectors
java.util.stream.Collectors,是从JDK1.8开始新引入的一个类。从源码的类注释上,我们可以知道:<u>Collectors实现了各种有用归约的操作,例如类型归类到新集合、根据不同标准汇总元素等。</u>透过示例,能让我们眼前一亮,短短的一行代码却能处理如此强大、复杂的功能:汇总、拼接、累加计算、分组等。
切记,不要用错哦,是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将成为集合的终极操作,其中,包括:
二、实战演练
1. 类型归类
将集合中元素按照类型、条件过滤等归类,存放到指定类型的新集合,List、Map、Set、Collection或者ConcurrentMap。涉及以下方法:
一般都作为终止操作符cololect的参数来使用,并伴随着流的结束。
常用于收集、筛选出集合(复杂集合)中的符合条件的数据,并存放于对应类型的新集合中,便于后续实际业务逻辑处理。
比如,将名字类型归类存在到List<String>集合中:
List<String> list = allPeoples.stream().map(People::getName).collect(Collectors.toList());
2. 分组
按照条件对元素进行分组,和 SQL 中的 group by 用法有异曲同工之妙,通常也建议使用Java代码进行分组处理以减轻数据库SQL压力。
分组涉及以下方法:
分组后,返回的是一个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()判断并取得最大或最小值。
涉及以下方法:
比如,找到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的使用过程中,你还遇到哪些好用、好玩的终极操作呢?
收藏
回复
删除帖子
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源
去关联
添加资源
相关推荐
-
Flutter 详解 (
六
、深入了解
Stream
)
柳随风
• 8693浏览
• 0回复
-
JDK
15 JAVA 15
的
新
特性
展望
七七QiQi
• 3501浏览
• 0回复
-
JDK
15 真的来了,一起来看看它
的
新
特性
柳随风
• 5022浏览
• 0回复
-
深入理解HashMap原理(一)——HashMap源码解析(
JDK
1.8
)
柳随风
• 4929浏览
• 0回复
-
JDK1.8
新
特性
(一):
JDK1.8
究竟有哪些
新
特性
呢
huatechinfo
• 7318浏览
• 0回复
-
JDK1.8
新
特性
(二):为什么要关注
JDK1.8
huatechinfo
• 5322浏览
• 0回复
-
JDK1.8
新
特性
(三):Lambda 表达式,让你爱不释手
huatechinfo
• 7591浏览
• 0回复
-
JDK1.8
新
特性
(四):函数式接口
桃太郎
• 7252浏览
• 0回复
-
JDK1.8
新
特性
(五):
Stream
,
集合
操作
利器,让你好用到飞起来
huatechinfo
• 9067浏览
• 0回复
-
JDK1.8
新
特性
(七):默认方法,真香,开动!接口?我要升级!!
detailtoo
• 4055浏览
• 0回复
-
JDK1.8
新
特性
(八):还在重复写空指针检查代码?赶紧使用 Opt
新新人类
• 4091浏览
• 0回复
-
【鸿蒙学院】鸿蒙IDE迎来重大更新,
新
特性
足以让你尖叫
蒙娜丽宁
• 1.0w浏览
• 12回复
-
「社区明星
集合
」社区明星名单
集合
贴,感谢感谢诸位
的
贡献!!
开源基础软件社区官方
• 2.8w浏览
• 0回复
-
DAYU200能打电话了—OpenHarmony 3.1
新
特性
!
润和软件HiHope
• 1.2w浏览
• 6回复
-
春暖花开
的
日子里,迎接鸿蒙
新
的
希望
鸿蒙钊哥
• 1.5w浏览
• 11回复
-
面试官:
JDK1.8
HashMap扩容rehash算法是如何优化
的
?(一)
pivoteic
• 3018浏览
• 0回复
-
面试官:
JDK1.8
HashMap扩容rehash算法是如何优化
的
?(二)
pivoteic
• 3075浏览
• 0回复
-
JAVA8
新
特性
函数式接口以及常用
的
Stream
流
操作
#冲刺创作新星#
掂掂三生有幸
• 1338浏览
• 0回复
-
别再问问
JDK
的
Stream
怎么用了好么?面试官
skyyoung
• 345浏览
• 0回复
帖子
视频
声望
粉丝
关注
最近发布
-
华为鸿蒙OS 2.0开发者Beta招募新增设备:Mate X2、Mate 40等...
2021-04-22 10:24:44发布
-
Dart 后台开发 Aqueduct 插入数据 获取数据 API
2021-04-01 13:44:07发布
相关问题
请教 sql 有关分组聚合问题?
1回答
OceanBase表分组是什么?
1回答