前言
近期,由于业务需要,会统计一些简单的页面指标,如果每个统计都通过SQL实现的话,又略感枯燥乏味。于是选择使用Stream的分组功能。对于这些简单的统计指标来说,Stream的分组更为灵活,只需要提取出需要统计的数据,便可以对这些数据进行任意处理,而无需再次编写不同的SQL去统计不同的指标。
此文主要是总结我在此前的工作中使用到的Collectors.groupingBy的一些方法和技巧。根据平时使用的习惯,将Collectors.groupingBy的功能大致分为四种,但这种界定都是模糊的,并不是绝对,每种功能都可以穿插使用,这里只是更方便了解Collectors.groupingBy各个方法的使用规则。
四种分组功能如下:
- 基础分组功能
- 分组统计功能
- 分组合并功能
- 分组自定义映射功能
Stream的其它用法可以参考下文:
语法说明
基础语法
Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)
- classifier:键映射:该方法的返回值是键值对的 键
- mapFactory:无参构造函数提供返回类型:提供一个容器初始化方法,用于创建新的 Map容器 (使用该容器存放值对)。
- downstream:值映射:通过聚合方法将同键下的结果聚合为指定类型,该方法返回的是键值对的 值。
前置数据
List<Student> students = Stream.of(
Student.builder().name("小张").age(16).clazz("高一1班").course("历史").score(88).build(),
Student.builder().name("小李").age(16).clazz("高一3班").course("数学").score(12).build(),
Student.builder().name("小王").age(17).clazz("高二1班").course("地理").score(44).build(),
Student.builder().name("小红").age(18).clazz("高二1班").course("物理").score(67).build(),
Student.builder().name("李华").age(15).clazz("高二2班").course("数学").score(99).build(),
Student.builder().name("小潘").age(19).clazz("高三4班").course("英语").score(100).build(),
Student.builder().name("小聂").age(20).clazz("高三4班").course("物理").score(32).build()
).collect(Collectors.toList());
分组的4种使用方法
1. 基础分组功能
说明:基础功能,分组并返回Map容器。将用户自定义的元素作为键,同时将键相同的元素存放在List中作为值。
Collectors.groupingBy:基础分组功能
下面的写法都是等价的
// 将不同课程的学生进行分类
Map<String, List<Student>> groupByCourse = students.stream().collect(Collectors.groupingBy(Student::getCourse));
Map<String, List<Student>> groupByCourse1 = students.stream().collect(Collectors.groupingBy(Student::getCourse, Collectors.toList()));
// 上面的方法中容器类型和值类型都是默认指定的,容器类型为:HashMap,值类型为:ArrayList
// 可以通过下面的方法自定义返回结果、值的类型
Map<