【问题标题】:HashMap null check in Merge Operation合并操作中的 HashMap 空值检查
【发布时间】:2018-02-27 06:44:12
【问题描述】:

为什么 HashMap 合并对值进行空值检查。 HashMap 支持空键和空值。那么有人可以告诉为什么需要对合并进行空检查吗?

@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();

因此,我无法使用Collectors.toMap(Function.identity(), this::get) 在地图中收集值

【问题讨论】:

标签: java-8 collectors


【解决方案1】:

该行为由the Map.merge contract 强制执行:

抛出:

NullPointerException - 如果指定的键为空且此映射不支持空键或者值或 remappingFunction 为空

请注意,将Map.merge 用于Collectors.toMap without a merge function 是一个实现细节;它不仅不允许 null 值,而且不提供报告重复键所需的行为,当存在重复键时,Java 8 实现错误地将两个值之一报告为键。

在 Java 9 中,实现已完全重写,不再使用 Map.merge。但是新的实现是行为兼容的,现在当值为null 时会显式地抛出代码。因此Collectors.toMap 不接受null 值的行为已在代码中修复,不再是使用Map.merge 的产物。 (还是说toMap 收集器没有合并功能而已。)

很遗憾,the documentation 不告诉你。

【讨论】:

  • 它不再使用Map#merge 是什么意思?我正在查看代码并实际看到它......
  • 如前所述,仍然谈到toMap 收集器,仅没有合并功能。寻找uniqKeysMapAccumulator。在对新值应用显式null 检查后,它使用putIfAbsent
  • 天哪!看了下代码,确实putIfAbsent的hack,至少它会正确报告问题的关键,谢谢你的回答
  • @Eugene 很好,它允许正确的故障报告,并且每个元素仍然只承载一个哈希操作。你还能得到什么……
  • 我不知道,只是每次我查看源代码我都期待超人代码
【解决方案2】:

因为 internally 对于Collectors.toMap,使用了Map#merge - 你真的无能为力。使用静态 Collectors.toMap 不是一种选择(顺便说一下,它被记录为抛出 NullPointerException)。

但是旋转一个自定义收集器来做你想做的事情(你没有展示)并不是那么复杂,这里有一个例子:

 Map<Integer, Integer> result = Arrays.asList(null, 1, 2, 3)
            .stream()
            .collect(
                    HashMap::new,
                    (map, i) -> {
                        map.put(i, i);
                    },
                    HashMap::putAll);

【讨论】:

  • 不幸的是,Collectors.toMap 的文档并没有真正说明 NullPointerExceptions。顺便说一句,您的 lambda 表达式实际上并不需要花括号。
【解决方案3】:

作为toMapmerge 中提到的空值问题的解决方法 您可以尝试通过以下方式使用自定义收集器:

public static <T, R> Map<T, R> mergeTwoMaps(final Map<T, R> map1,
                                            final Map<T, R> map2,
                                            final BinaryOperator<R> mergeFunction) {
    return Stream.of(map1, map2).flatMap(map -> map.entrySet().stream())
            .collect(HashMap::new,
                    (accumulator, entry) -> {
                        R value = accumulator.containsKey(entry.getKey()) 
                                ? mergeFunction.apply(accumulator.get(entry.getKey()), entry.getValue())
                                : entry.getValue();
                            accumulator.put(entry.getKey(), value);
                    },
                    HashMap::putAll);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-29
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多