1 问题复现
使用stream的toMap方法从List<对象A>转Map<String, String>,
当对象A中的属性值为null的属性转换为Map的value时,使用toMap会抛出异常。
即:
对象:UserEntity属性nickname值为null,将nickname作为Map的value,uid作为key,抛异常的代码片段如下:
List<UserEntity> userEntityList1 = new ArrayList<>();
userEntityList1.add(new UserEntity("1", null, "male"));
Map<String, String> map1 = new HashMap<>();
map1 = Optional.ofNullable(userEntityList1).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(UserEntity::getUid, UserEntity::getNickname, (oldVal, newVal) -> oldVal));
- 测试样例
package lambda_expression;
import common.entity.BaseUserEntity;
import common.entity.UserAgeEntity;
import common.entity.UserEntity;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Stream数据转换测试.
*
* @author xindaqi
* @date 2021-06-28 17:20
*/
public class DataTransformStreamTest {
private static final Logger logger = Logger.getLogger("StreamTest");
public static void main(String[] args) {
List<UserEntity> userEntityList1 = new ArrayList<>();
userEntityList1.add(new UserEntity("1", null, "male"));
Map<String, String> map1 = new HashMap<>();
map1 = Optional.ofNullable(userEntityList1).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(UserEntity::getUid, UserEntity::getNickname, (oldVal, newVal) -> oldVal));
logger.info("map1:" + map1);
if(null != map1.get("1")) {
logger.info("map1 not empty");
} else {
logger.info("map1 is empty");
}
}
}
- 异常
Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1225)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at lambda_expression.DataTransformStreamTest.main(DataTransformStreamTest.java:176)
2 原因
toMap源码:
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
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);
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
- 分析
由源码可知,toMap的中调用的merge方法中对象要求:Objects.requireNonNull(value);可知,Map的value不可为null,因此,使用toMap方式转换Map时,值value不能为null。
3 方案
使用collect(HashMap::new…)方案,避免值为null抛空指针。
map1 = Optional.ofNullable(userEntityList1).orElse(new ArrayList<>()).stream().collect(HashMap::new, (k, v) -> k.put(v.getUid(), v.getNickname()), HashMap::putAll);
4 附
当key为null时,使用toMap不会抛空指针。