活久见,java8 lamdba Collectors.toMap()报NPE

前言

事情是这样的,今天调试程序时,有个功能是需要将查询的结果集:List> 中的key转换成java的驼峰命名规则模式,比如:USER_NAME => userName 。代码如下:

List> result = query();
result = result.stream().map(m -> m.entrySet().stream()
.collect(Collectors.toMap(e -> JavaUtil.getJavaModeColumnName(e.getKey()), Map.Entry::getValue)))
                    .collect(Collectors.toList());
复制代码

然后就遇到了如下报错信息:

一开始的时候还挺郁闷的,用了这么lamdba的API,也没遇到这个错误啊,这是为什么呢?我们去查看这行:

HashMap.merge(HashMap.java:1225) ~[na:1.8.0_201]的源代码是什么:

判断是:如果value==null,就抛出NPE。

过程分析

复现

Map objectMap = new HashMap<>();
objectMap.put("ADC_ADC", null);
// 报错: value 不能为空
objectMap.entrySet().stream().collect(
    Collectors.toMap(e -> e.getKey().toLowerCase(), Map.Entry::getValue));
复制代码

上述代码复现了我们上面提到的原始问题出现的错误,我们分析一下调用栈和查看jdk源码就会发现:

Collectors.toMap中调用了这个方法:

而上述方法中的 BiConsumer accumulator 调用了 map.merge()。

问题找到了,那么怎么解决呢?

处理方式

既然是accumulator方法有问题,那么我们就替换掉accumulator。我们找一下 Collectors类中的toMap()方法并没有提供可以传入accumulator参数的方法。那么我们退而求其次,从collect()方法下手,代码如下:

Map objectMap = new HashMap<>();
objectMap.put("USER_NAME", null);

Map collect = objectMap.entrySet().stream()
        .collect(HashMap::new, 
                 (map, entry) -> map.put(Util.getJavaModeColumnName(entry.getKey()), entry.getValue()),
                 HashMap::putAll);
复制代码

R collect(Supplier supplier,

BiConsumer accumulator,

BiConsumer combiner);

还函数的参数都是什么意思呢:

supplier: 用于创建容器的工厂函数。在收集元素之前,该函数将被调用一次,用于创建一个空的容器。

accumulator: 用于将 Stream 中的元素添加到容器中的累加器函数。该函数接受两个参数,第一个参数是容器,第二个参数是 Stream 中的元素。该函数将 Stream 中的每个元素添加到容器中。

combiner: 用于合并两个容器的函数。在多个线程并行执行收集操作时,将在每个线程中创建一个容器,并使用该函数将它们合并为一个容器。如果 Stream 是串行的,该函数将不会被调用。

那么在我们使用lamdba的收集器,jdk自带的不能满足我们的需求时,我们就可以使用该方法自定义手机规则和模式。

官方示例:String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,StringBuilder::append).toString(); 将数据拼接 。

运行结果如下图:

这种方式的问题是:就是当你的key值重复的时候,这将会覆盖上一个值。但是在我当前的使用场景下是不存在的,因为表名称字段肯定是唯一的。

其他

网上有讨论说:更新JDK,我用JDK16,JDK17时再次测试改代码时,都无法得到正确的答案。

遇到这个问题时,我就在想,java为什么要如此设计?value的值为什么不能为null?

后来我搜索了相关问题的讨论,在 Stack Overflow上找到了这篇:

stackoverflow.com/questions/4…

其中有一段讨论是这么说的:

大意就是:没想到null值对API的影响如此之大,有人则认为这是一个jdk的缺陷。

这之后我不死心,之后我找到了在官网上的讨论的:bugs.openjdk.org/browse/JDK-…

不幸的是:目前该bug并未被解决,不论出于什么原因。这与我在JDK16,JDK17下的测试是保持一致的。

后续

大家也可以看一下下面这个示例的返回值是什么:

List list = new ArrayList<>();
list.add(new User(12, "张三"));
list.add(new User(16, "李四"));
list.add(new User(16, "王五"));
Map collect1 = list.stream().
    collect(Collectors.toMap(User::getAge, User::getName));


原文链接:https://juejin.cn/post/7221470013872980024

展开阅读全文

页面更新:2024-04-16

标签:累加器   示例   线程   容器   函数   元素   规则   参数   代码   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top