背景
最近在某大厂线下面试二面的时候,简历里写的"熟练掌握JDK8新特性,熟练使用Stream流、Lambda表达式等"给自己埋了个坑,面试官拿笔在Stream流上给圈了一下,并抛出了一个问题,假设现在我从数据库里查出了一个List集合,也就是多行记录,他们的A字段都是一样的,B字段不同,如何实现将这个List用Stream流转成Map,以A字段作为key,B字段作为Value,相同A字段对应的不同B字段放到一个list里,也就是实现List=> Map<A,List<B字段>>的转换
这问题我熟啊,平常开发也经常用,不用Stream流用for循环也就一次遍历,可stream流偏偏我忘了,当时只记得是在筛选的时候,用了个啥groupBy分组,但是分组方法怎么写我忘了,给面试官留下不好印象了哎好可惜…于是乎我决定痛定思痛,好好记录一下。
解决
正常普通实现
首先正常用for循环也就一次遍历,假设现在list中的元素就是一个包含两元素的一维数组,其中第一个元素对应问题中的A字段,第二个元素对应问题中的第二个字段。
List为[ [1,2], [1,3], [2,1], [2,2], [2,3] ],那么代码就如下所示:
Map<Integer,List<Integer>> map = new HashMap<>();
for(int[] arr:list){
map.computeIfAbsent(arr[0], key -> new ArrayList<>()).add(arr[1]);
}
//结果为:1=>[2, 3] 2=>[1, 2, 3]
Stream流实现
我去翻了翻自己以前写的代码,原来是Collectors.groupingBy()方法,然后用Collectors.mapping第二个元素映射成List集合,代入这个示例下的代码如下所示:
Map<Integer, List<Integer>> map = list.stream()
.collect(Collectors.groupingBy(
arr -> arr[0],
Collectors.mapping(arr -> arr[1], Collectors.toList())
));
仔细想想,这方法是我让chatGpt帮忙写的,可读性差,还要记这方法名,难怪自己线下写不出,然后老老实实去巩固了下toMap的基础用法,以后还是踏踏实实这么写把,可读性好,易理解,也通用,下次面试也不至于不会写!!
Map<Integer,List<Integer>> streamMap = list.stream().collect(Collectors.toMap(
arr->arr[0],
arr->new ArrayList<>(Arrays.asList(arr[1])),
(oldList,newList)->{
newList.addAll(oldList);
return newList;
}));
直接使用toMap()方法,key指定为arr[0],value指定为一个初始值为arr[1]的List集合,当key相同时,执行第三个参数,也是一个方法,传入旧value,新value也就是上一个key对应的list和当前key对应的list,方法内执行的操作就是合并两集合,这样不管今后有什么操作,都不用拘泥于去找对应的方法api名字了