在SQL中经常会用到分组,我们也常常遇到一些组合分组的场景。
java8分组 传统写法(单个字段分组)
场景:根据 城市 进行分组
使用的是方法引用:User::getCity 来完成分组
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class Demo2 {public static void main(String[] args) {DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// data listList userList = Arrays.asList(User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build());Map> groupMap = userList.stream().collect(Collectors.groupingBy(User::getCity));groupMap.forEach((k, v) -> {System.out.println(k);System.out.println(v);});}
}
java8分组 传统写法(多个字段分组)
场景:根据 城市,性别进行分组
一般的写法会是下面的这种写法,通过lambda表达式将key的生成逻辑传入进去:u -> u.getCity() + “|” + u.getSex() 来实现分组的效果。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class Demo2 {public static void main(String[] args) {DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// data listList userList = Arrays.asList(User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build());Map> groupMap = userList.stream().collect(Collectors.groupingBy(u -> u.getCity() + "|" + u.getSex()));groupMap.forEach((k, v) -> {System.out.println(k);System.out.println(v);});}
}
分析:多个分组条件 与 单个分组条件 两种写法
单个条件的分组用的比较多,userList.stream().collect(Collectors.groupingBy(User::getCity));
这种方法引用的方式看起来很清爽。
在我们遇到多个字段的分组的时候,我并不太想使用前面那种传统的写法①。
我在想,既然单个字段的分组写法是:
userList.stream().collect(Collectors.groupingBy(User::getCity));
那么多个字段的写法可否是下面这种( 类推 ),传入多个方法引用!
userList.stream().collect(Collectors.groupingBy(User::getCity,User::getSex));
很可惜 jdk 类库中Collectors 没有提供这种写法
因为jdk没有提供这种写法,于是自己就想写了一个Util来帮助我们使用多个方法引用的方式完成组合分组
MyBeanUtil groupingBy(userList, User::getSex, User::getCity);
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;public class MyBeanUtil {public static void main(String[] args) {DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// data listList userList = Arrays.asList(User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build());// 进行分组,根据名字和城市分组Map> groupMap = groupingBy(userList, User::getSex, User::getCity);//打印分组结果groupMap.forEach((k, v) -> {System.out.println(k);System.out.println(v);});}/*** 将数据分组,根据方法引用(bean的get方法)** @param list 为分组的数据* @param functions get方法数组*/@SafeVarargspublic static Map> groupingBy(List list, Function... functions) {return list.stream().collect(Collectors.groupingBy(t -> groupingBy(t, functions)));}/*** 分组工具根据函数式接口使用分组,将数据根据分组结果进行拆分*/@SafeVarargspublic static String groupingBy(T t, Function... functions) {if (functions == null || functions.length == 0) {throw new NullPointerException("functions数组不可以为空");} else if (functions.length == 1) {return functions[0].apply(t).toString();} else {return Arrays.stream(functions).map(fun -> fun.apply(t).toString()).reduce((str1, str2) -> str1 + "|" + str2).get();}}
}
再度优化
依然不是很满足这种写法,因为这种写法需要借助 Util 类,不够接地气!
更希望是下面这种接地气的写法:能够完全集成在jdk类库中
点击下方链接,查看原文对于jdk源代码修改的写法
再度优化,修改源码