Map.ofEntries 在使用 containsKey() 检查 NULL 键时给出空指针异常 不可修改的地图

我之前使用 HashMap 从常量映射中获取键。

containsKey() 处传递 NULL 键时,我曾经得到 FALSE

为了让代码看起来很花哨,我尝试了 java-8。因此,我开始使用 Map.ofEntries 来构建我的地图

,而不是 HashMap

令人惊讶的是,当 Null 键被传递给 containsKey() 方法时,我得到了Null Pointer Exception

String str = null;

Map<String,String> hashMap = new HashMap<>();
hashMap.put("k1","v1");
System.out.print(hashMap.containsKey(str)); // This gives false

Map<String,String> ofEntriesMap = Map.ofEntries( Map.entry("k1","v1")); 
System.out.print(ofEntriesMap.containsKey(str)); // Why this gives Null Pointer Exception ?

我无法弄清楚为什么它在 Map.ofEntries 处的行为不同。

处理这种情况的最佳方法是什么?

JeeRain 回答:Map.ofEntries 在使用 containsKey() 检查 NULL 键时给出空指针异常 不可修改的地图

Map 的 javadoc 说:

不可修改的地图

Map.ofMap.ofEntriesMap.copyOf 静态工厂方法提供了一种创建不可修改映射的便捷方法。这些方法创建的 Map 实例具有以下特点:

  • 他们是unmodifiable。不能添加、删除或更新键和值。调用 Map 上的任何 mutator 方法总是会导致抛出 UnsupportedOperationException。但是,如果包含的键或值本身是可变的,这可能会导致 Map 行为不一致或其内容似乎发生变化。
  • 它们不允许 null 键和值。尝试使用 null 键或值创建它们会导致 NullPointerException
  • ...

相反,HashMap 的 javadoc 说:

基于哈希表的 Map 接口实现。此实现提供了所有可选的映射操作,并允许 null 值和 null。 (HashMap 类大致等同于 Hashtable,只是它是不同步的并且允许空值。)该类不保证映射的顺序;特别是,它不保证订单会随着时间的推移保持不变。

...

,

代替 HashMap,我开始使用 Map.ofEntries 来构建我的地图

令人惊讶的是,我在传递 Null 键时遇到了Null Pointer Exception 到 containsKey() 方法

java.util.Map 的文档部分说明:

某些地图实现对它们可能包含的键和值有限制。例如,某些实现禁止 null 键和值,有些对其类型有限制 键。尝试插入不合格的键或值会引发 未经检查的异常,通常是 NullPointerExceptionClassCastException试图查询一个 不合格的键或值可能会引发异常,或者可能只是 返回假;一些实现会表现出以前的行为 有些会展示后者。

(添加了强调。)

正如 @Andreas's answer 已经观察到的,通过 Map.ofEntries() 创建的地图就是这样的实现。具体来说,它们不允许空键和值。尽管没有记录它们的 containsKey() 方法是否在提供 null 参数时行使抛出选项,但您需要在使用它们时考虑到这种可能性。

另一方面,正如 Andreas 还表明的,HashMap 被记录为允许空键和值,因此当传递空参数时,它的 containsKey() 方法预计会正常完成。

处理这种情况的最佳方法是什么?

您有两个主要选择:

  • 如果您想继续(直接)使用通过 Map.ofEntries() 创建的地图,那么您必须避免测试它是否包含空键。既然你知道它不能包含空键,这样的测试是不必要的。

  • 如果您希望能够测试地图中是否存在空键,特别是如果您想要在其中包含空键或空值的选项,那么您不得使用 {{1}来创建它。但是,您可以使用 Map.ofEntries()初始化它。例如:

    Map.ofEntries()

    另请注意,如果您在地图中放置的条目少于 11 个,那么 Map<String,String> myMap = Collections.unmodifiableMap( new HashMap<String,String>( Map.ofEntries( Map.Entry("k1","v1") ) ) ); 会比 Map.of() 更整洁一些。而且,当然,如果您不关心地图是否可修改,那么您不必将其放入不可修改的包装器中。

,

这是由 Map.ofEntries 创建的不可修改映射的实现细节。

当您将 null 键添加到 HashMap 时,它计算 null 的哈希值等于 0

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

但是 Map.ofEntries 会在只提供一对的情况下创建 ImmutableCollections.Map1,否则会创建 ImmutableCollections.MapN

这是 ImmutableCollections.Map1::containsKey

的实现
public boolean containsKey(Object o) {
    return o.equals(k0); // implicit nullcheck of o
}

您可以看到该评论指出 NullPointerException 是预期行为。至于 ImmutableCollections.MapN::containsKey 它使用显式空检查。

public boolean containsKey(Object o) {
        Objects.requireNonNull(o);
        return size > 0 && probe(o) >= 0;
}

如果您参考 Map::containsKey Javadoc,您可以看到它明确表示此方法可能会或可能不会产生 NPE。

如果此映射包含指定键的映射,则返回 true。更多的 形式上,当且仅当此映射包含键 k 的映射使得 Objects.equals(key,k) 时,才返回 true。 (最多可以有一个这样的映射。)

参数: key – 要测试其在此地图中是否存在的键

返回: 如果此映射包含指定键的映射,则为 true

抛出: ClassCastException – 如果该键的类型不适合此地图(可选)

NullPointerException – 如果指定的键为空并且此映射不允许空键(可选)

本文链接:https://www.f2er.com/14110.html

大家都在问