合并操作中的 HashMap 空检查

HashMap null check in Merge Operation

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

@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) 在地图

中收集值

因为 内部 用于 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);

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

Throws:

NullPointerException - if the specified key is null and this map does not support null keys or the value or remappingFunction is null

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

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

可惜,the documentation没说。

作为 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);
}