[英]Map.ofEntries gives Null Pointer Exception on checking NULL key using containsKey()
我之前使用 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
表现不同。
处理这种情况的最佳方法是什么?
Map
的 javadoc 说:
不可修改的地图
Map.of
、Map.ofEntries
和Map.copyOf
静态工厂方法提供了一种创建不可修改映射的便捷方法。 这些方法创建的 Map 实例具有以下特点:
- 它们是不可修改的。 不能添加、删除或更新键和值。 调用 Map 上的任何 mutator 方法将始终导致抛出
UnsupportedOperationException
。 但是,如果包含的键或值本身是可变的,这可能会导致 Map 行为不一致或其内容似乎发生变化。- 它们不允许
null
键和值。 尝试使用null
键或值创建它们会导致NullPointerException
。- ...
相比之下, HashMap
的 javadoc 说:
Map
接口的基于哈希表的实现。 此实现提供了所有可选的映射操作,并允许null
值和null
键。 (HashMap
类大致等同于Hashtable
,除了它是非同步的并且允许空值。)该类不保证映射的顺序; 特别是,它不保证订单会随着时间的推移保持不变。...
而不是 HashMap,我开始使用 Map.ofEntries来构建我的地图
令人惊讶的是,当 Null 键被传递给 containsKey() 方法时,我得到了Null Pointer Exception
java.util.Map
的文档部分说:
一些地图实现对它们可能包含的键和值有限制。 例如,有些实现禁止空键和值,有些实现对其键的类型有限制。 尝试插入不合格的键或值会引发未经检查的异常,通常为
NullPointerException
或ClassCastException
。 尝试查询不合格的键或值的存在可能会引发异常,或者可能只是返回 false; 一些实现会表现出前一种行为,而另一些会表现出后者。
(加了重点。)
正如Map.ofEntries()
的回答已经观察到的那样,通过Map.ofEntries()
创建的地图就是这样的实现。 具体来说,它们不允许空键和值。 尽管没有记录它们的containsKey()
方法是否在提供 null 参数时执行抛出选项,但您需要在使用它们时考虑到这种可能性。
另一方面,正如 Andreas 还表明的, HashMap
被记录为允许空键和值,因此当传递空参数时,它的containsKey()
方法有望正常完成。
处理这种情况的最佳方法是什么?
您有两个主要选择:
如果您想继续(直接)使用通过Map.ofEntries()
创建的地图,那么您必须避免测试它是否包含空键。 由于您知道它不能包含空键,因此无需进行此类测试。
如果您想依靠能够测试您的地图中是否存在空键,特别是如果您想要在其中包含空键或空值的选项,那么您一定不要使用Map.ofEntries()
来创建它。 但是,您可以使用Map.ofEntries()
进行初始化。 例如:
Map<String, String> myMap = Collections.unmodifiableMap( new HashMap<String, String>( Map.ofEntries( Map.Entry("k1", "v1") ) ) );
另请注意,如果您在地图中放置的条目少于 11 个,则Map.of()
比Map.ofEntries()
整洁。 而且,当然,如果您不关心地图是否可修改,那么您不必将其放入不可修改的包装器中。
这是由Map.ofEntries
创建的不可修改映射的实现细节。
当您将null
键添加到HashMap
,它会计算null
哈希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 – 如果指定的键为空并且此映射不允许空键(可选)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.