现在Stream Api极为常用,本文记录了list转化为map(Collectors.toMap)容易出现的坑。
1、若list中有多个元素对应的key相同,若没有处理,会抛java.lang.IllegalStateException: Duplicate key异常
2、若value值为null,报NullPointerException。
首先我们先看一下Collectors.toMap的定义:
Collectors.toMap有多个方法重载,如下:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);参数含义分别是:
keyMapper:Key的映射函数,用来生成key
valueMapper:Value的映射函数,用来生成value
mergeFunction:当Key冲突时,调用的合并方法
mapSupplier:Map构造器,在需要返回特定的Map时使用
范例:
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "aAA", "bBb", "CCc");
Map<String, String> dataMap = list.stream()
.collect(Collectors.toMap(s -> s.toUpperCase(), s -> s.toLowerCase()));异常:

按照常规思维,往一个map里put一个已经存在的key,会把原有的key对应的value值覆盖。但是在使用Java8中的Collectors.toMap时,它默认给抛异常,抛异常 java.lang.IllegalStateException: Duplicate key...。
分析:


解决方案:
此时就需要使用到toMap的第三个参数了,自定义mergeFunction,如下面的 (value1,value2)->value1,
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "aAA", "bBb", "CCc");
Map<String, String> dataMap = list.stream()
.collect(Collectors.toMap(s -> s.toUpperCase(), s -> s.toLowerCase(), (value1,value2)->value1));使用到的类
@Data
public class Person{
private Long id;
private String name;
public Person(Long id, String name) {
this.id = id;
this.name = name;
}
}案例:
List<Person> list = Arrays.asList(new Person(3L, "张三"), new Person(4L, "李四"), new Person(5L, null));
Map<Long, String> dataMap = list.stream()
.collect(Collectors.toMap(Person::getId, Person::getName, (v1, v2) -> v1));异常:

分析:
在使用toMap时,是使用map.merge来合并数据的,而在merge时,如果value为空则跑出NPE异常。


解决方案:
// 方案一,先过滤掉空值
List<Person> list = Arrays.asList(new Person(3L, "张三"), new Person(4L, "李四"),
new Person(5L, null));
Map<Long, String> dataMap = list.stream().filter(o -> Objects.nonNull(o.getName()))
.collect(Collectors.toMap(Person::getId, Person::getName));
//方案二,使用Optional,在取值时不能直接使用get,optional.get()对于空值会抛NPE
List<Person> list = Arrays.asList(new Person(3L, "张三"), new Person(4L, "李四"),
new Person(5L, null));
Map<Long, Optional<String>> dataMap = list.stream().collect(
Collectors.toMap(Person::getId, o -> Optional.ofNullable(o.getName()))
);
System.out.println(map.get(3L).get());
System.out.println(map.get(5L).orElse(null));
//方案三,自定义collect
List<Person> list = Arrays.asList(new Person(3L, "张三"), new Person(4L, "李四"),
new Person(5L, null));
Map<Long, String> dataMap = list.stream().collect(HashMap::new,
(map, element) -> map.put(element.getId(), element.getName()), HashMap::putAll);