环境说明
- JDK1.8
- Collectors.toMap()方法
问题复现
当使用Stream的collect()收集的时可以传入自定义收集器,这个时候就可以使用Collectors工具类中的各种生成自定义收集器的静态方法。toMap()就是用来生成可以收集指定key和value的收集器的。
@Test
public void toMap_Test(){
person person1=new person();
person1.name="张三";
person1.address="北京";
person person2=new person();
person2.name="李四";
person2.address=null; //注意
person person3=new person();
person3.name="张三";
person3.address="广州";
HashMap<String, String> NameAddressMap = Arrays.asList(person1, person2, person3).stream().
collect(Collectors.toMap(person::getName, person::getAddress, (olderValue, replaceValue) -> olderValue, HashMap::new));
}
报错:
toMap()源码
//其他的重载方法底层调用也是这个方法
/**
Params: keyMapper:key的映射函数
valueMapper:value的映射函数
mergetFunction:合并函数(当向map中插入存在的Key的时候,使用该函数提供的解决方案)
mapSupplier:map实例提供函数
*/
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}
这里提供的Map是HashMap,不管key是否发生冲突,总会调用响应map实现类的中的merge()方法来合并数据,mege()方法会检查元素的value是否为null,而不是key为null。而且HashMap中key为null是可以保存的,所以当你收集的数据中指定的value的这个字段如果为null就会报NullPointerException。
//HashMap中的merge方法的部分代码
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException();
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
....
}
解决方案
如果没有提供mergeFunction方法的话,当key重新重复的时候,默认使用throwingMerger()抛出异常
在收集前过滤掉指定value的字段为null的数据,把NullPointerException异常提前,这样就可以根据日志更好的定位问题。
HashMap<String, String> NameAddressMap = Arrays.asList(person1, person2, person3).stream().
filter(person -> {
if(person.getAddress()==null){
log.error("{}数据为null,请检查",person.getName());
}
return true;
}).
collect(Collectors.toMap(person::getName, person::getAddress, (olderValue, replaceValue) -> olderValue, HashMap::new));