【问题标题】:NullPointerException: element cannot be mapped to a null keyNullPointerException:元素无法映射到空键
【发布时间】:2018-05-31 19:40:19
【问题描述】:

我已阅读该主题:

Collectors.groupingBy doesn't accept null keys

但我不明白如何将其应用于我的问题:

我的代码:

Map<String, List<MappingEntry>> mappingEntryMap = mapping.getMappingEntries()
                .stream()
                .collect(Collectors.groupingBy(MappingEntry::getMilestone, Collectors.mapping(e -> e, Collectors.toList())));

对我来说MappingEntry::getMilestone 有时可以返回 null。我的情况没问题,但我明白了:

Caused by: java.lang.NullPointerException: element cannot be mapped to a null key
    at java.util.Objects.requireNonNull(Objects.java:228)
    at java.util.stream.Collectors.lambda$groupingBy$45(Collectors.java:907)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    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)

如何避免此异常?

【问题讨论】:

  • 先过滤空值怎么样?
  • @Eugene 这对我来说是不允许的。我不能丢失数据。链接主题中描述的这种方法
  • @gstackoverflow 为所有 null 键应用默认键怎么样?
  • 也可以将Collectors.mapping(e -&gt; e, Collectors.toList())简化为Collectors.toList()
  • @Lino … 和 Collectors.toList() 在使用 Collectors.groupingBy 时可以省略。

标签: java java-8 grouping java-stream


【解决方案1】:

改用Collectors.toMap 并指定使用HashMap(因为它允许一个空键)

 Collectors.toMap(
       MappingEntry::getMilestone,
       x -> {
           List<MappingEntry> list = new ArrayList<>();
           list.add(x);
           return list;
       },
       (left, right) -> {
            left.addAll(right);
            return left;
       },
       HashMap::new

)

【讨论】:

  • 不是一个漂亮的解决方案。您更改了数据结构和逻辑。不配成为最佳答案。
  • @RajeshPaul 至少有 17 人不同意你的观点。
【解决方案2】:

您可以为此使用Collector.of 方法来指定自定义收集器并使用HashMap 生成地图,因为根据HashMapJava-8 文档

[...] 允许空值和空键。 [...]

Collector.of(
    HashMap::new, 
    (map, e) -> map.computeIfAbsent(e.getMileStone(), k -> new ArrayList<>()).add(e), 
    (left, right) -> {
         right.forEach((key,list) -> left.computeIfAbsent(key, k -> new ArrayList<>()).addAll(list));
         return left;
     })
)

【讨论】:

    【解决方案3】:

    假设您想保留MappingEntry 对象,无论getMilestone() 何时为null非null 并且知道NullPointerException 将被抛出当未满足特定合同时,我们可以通过使用替换键对具有 null 里程碑的 MappingEntry 对象进行分组,然后将其他 MappingEntry 对象按预期分组来避免这种情况成为。

    Map<String, List<MappingEntry>> mappingEntryMap = 
                 mapping.getMappingEntries()
                        .stream()
                        .collect(groupingBy(m -> m.getMilestone() == null ?
                                      "absentMilestone" : m.getMilestone()));
    

    这里的技巧是使用三元运算符,它提供了一个键来将所有具有不存在里程碑的MappingEntry对象分组到一个组中,如果里程碑不存在,那么我们可以按照您的预期按其值分组。

    【讨论】:

    • 这是绝对正确的答案,值得被选为最佳答案。谢谢@Ousmane D。
    【解决方案4】:

    我认为最好的解决方案:

    使用 Optional.empty() 而不是传递空值。

    Map<Optional<String>, List<MappingEntry>> mappingEntryMap = mapping.getMappingEntries()
                .stream()
                .collect(Collectors.groupingBy(Optional::ofNullable, Collectors.mapping(e -> e, Collectors.toList())));
    

    【讨论】:

      【解决方案5】:

      使用过滤器,只获取非空数据。

      喜欢

                  Map<String, List<Entity>> map = list
                      .stream()
                      .filter(entity -> entity.getZoneRefId()!=null)
                      .collect(
                              Collectors.
                                      groupingBy(
                                              Entity::getZoneName));
              
      

      【讨论】:

        猜你喜欢
        • 2018-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-09-27
        • 2011-03-07
        相关资源
        最近更新 更多