Java8之Stream流的Collectors.toMap避坑指南
Java8之Stream流的Collectors.toMap避坑指南
一 背景:
笔者在之前的工作中,遇到了一个需求,大概是需要将对象的List转为Map方便下一步的数据操作,在这里当我询问了一些同事得到的回答为Map中的key是唯一的,但上线后仍然在Stream流的这一行报错,错误为下图
笔者也很纳闷,看错误提示明显为toMap转换的时候Key重复了,但笔者百思不得其解,人与人的信任呢?是谁给我说的不可能重复,唯一的,笔者已经去准备好好收拾一下这位同事了,但话说回来,难道我们就不能杜绝Key重复的情况?如果不从业务上解决,如何从代码上解决呢?
二 toMap源码
在笔者思考这个问题的时候,看到了toMap的源码,这样一来,就清楚多了,我们可以看到,toMap有三个重载方法,分为别:
两个参数的:
三个参数的:
四个参数的:
这四个参数的具体含义为:
1 keyMapper:Key 的映射函数。
2 valueMapper:Value 的映射函数。
3 mergeFunction:当 Key 冲突时,调用的合并方法。
4 mapSupplier:Map 构造器,在需要返回特定的 Map 时使用。`
下面我们来细看为什么两个参数会报错以及如何解决和三个构造方法的演示:
三 两个参数的toMap:
1当id即key不重复的情况:
public static void main(String[] args) {
List<User> list = Arrays.asList(
new User().setId("1").setName("张小"),
new User().setId("2").setName("李大"),
new User().setId("3").setName("小明")
);
//2个参数的toMap方法
Map<String, String> map = list.stream()
.collect(Collectors.toMap(User::getId, User::getName));
System.out.println(map);
}
运行:
2当id即key重复的情况:
public static void main(String[] args) {
List<User> list = Arrays.asList(
new User().setId("1").setName("张小"),
new User().setId("2").setName("李大"),
new User().setId("2").setName("小明")
);
//2个参数的toMap方法
Map<String, String> map = list.stream()
.collect(Collectors.toMap(User::getId, User::getName));
System.out.println(map);
}
细看两个参数的源码我们可以发现:
走到这里就能看到如果oldValue有值就会执行抛出异常的方法。
四 三个参数的toMap(mergeFunction):
运行:
我们可以看到当Key重复,Value的值进行了处理,变成了两个Key,重复的Key合并了拼接的Value值。至于ID不重复的情况,笔者这里没有写,因为运行都是正常的,但是需要注意的是:
1 HashMap对象的key、value值均可为null。
2 toMap的话,value为null,也是会抛异常-空指针异常!
看一下示例:
public static void main(String[] args) {
List<User> list = Arrays.asList(
new User().setId("1").setName("张小"),
new User().setId("2").setName("李大"),
new User().setId("2")
);
//2个参数的toMap方法
Map<String, String> map = list.stream()
.collect(Collectors.toMap(User::getId, User::getName,(v1, v2) -> v1 + "-" + v2));
System.out.println(map);
}
}
运行:
我们看源码:
看到这里,想必各位已经明白了,那么怎么解决呢?我们可以在toMap方法里加一个三目运算符防止出现空指针报错,例如:
运行:
五 四个参数的toMap
第四个参数(mapSupplier)用于自定义返回 Map 类型,例如:
public static void main(String[] args) {
List<User> list = Arrays.asList(
new User().setId("1").setName("张小"),
new User().setId("2").setName("李大"),
new User().setId("2")
);
//4个参数的toMap方法
HashMap<String, String> collect = list.stream().collect(
Collectors.toMap(
User::getId,
u -> u.getName() == null ? "未知" : u.getName(),
(v1, v2) -> v1 + "、" + v2,
HashMap::new));
}
}
这里笔者就返回了HashMap,我们也可以利用这第四个参数利用Map的特性去进行业务上的处理和操作。
六 总结
经过这一次线上BUG的产生,笔者明白了一个道理,就算别人拍着胸脯给你说不可能重复,绝对是唯一,也要做好校验,除非是那种key为数据库表中的ID的,那再做校验也就有点多此一举,我们需要注意的无非就是以下三点:
1 toMap中Key重复的情况怎么处理
2 toMap中的Value为null的情况怎么处理
这两个考虑到了,那么恭喜你,你又比别人(指的笔者)少加班半个小时,恭喜你!
那么这一期的踩坑就到这里了,下次再见,米娜桑~
敬礼!!!
salute!!!