[英]Why does ConcurrentHashMap prevent null keys and values?
ConcurrentHashMap
的 JavaDoc 是这样说的:
像
Hashtable
但不同于HashMap
,这个类不允许null
用作键或值。
我的问题:为什么?
第二个问题:为什么Hashtable
不允许为空?
我使用了很多 HashMap 来存储数据。 但是当更改为ConcurrentHashMap
时,由于 NullPointerExceptions,我遇到了几次麻烦。
来自ConcurrentHashMap
的作者本人(Doug Lea) :
ConcurrentMaps(ConcurrentHashMaps、ConcurrentSkipListMaps)中不允许空值的主要原因是无法适应在非并发映射中几乎不能容忍的歧义。 主要的一个是,如果
map.get(key)
返回null
,您无法检测键是否显式映射到null
与键未映射。 在非并发映射中,您可以通过map.contains(key)
检查这一点,但在并发映射中,映射可能在调用之间发生了变化。
我相信这是,至少部分,让你结合containsKey
并get
到一个单一的电话。 如果映射可以保存空值,则无法判断get
是否返回空值是因为该值没有键,或者仅仅是因为该值是空值。
为什么这是一个问题? 因为没有安全的方法可以自己做到这一点。 取以下代码:
if (m.containsKey(k)) {
return m.get(k);
} else {
throw new KeyNotPresentException();
}
由于m
是并发映射,因此键 k 可能会在containsKey
和get
调用之间被删除,从而导致此代码段返回一个从未出现在表中的空值,而不是所需的KeyNotPresentException
。
通常你会通过同步来解决这个问题,但是使用并发映射当然是行不通的。 因此get
的签名必须改变,以向后兼容的方式做到这一点的唯一方法是首先防止用户插入空值,并继续使用它作为“未找到密钥”的占位符。
Josh Bloch 设计了HashMap
; Doug Lea 设计了ConcurrentHashMap
。 我希望这不是诽谤。 实际上我认为问题在于空值通常需要包装,以便真正的空值可以代表未初始化。 如果客户端代码需要空值,那么它可以支付包装空值本身的(不可否认的小)成本。
您不能在 null 上同步。
编辑:在这种情况下,这并不是为什么。 我最初认为针对并发更新锁定事物或以其他方式使用对象监视器来检测某些内容是否被修改是很有趣的事情,但是在检查源代码时发现我错了 - 他们使用基于哈希的位掩码。
在那种情况下,我怀疑他们这样做是为了复制 Hashtable,我怀疑 Hashtable 这样做是因为在关系数据库世界中,null != null,因此使用 null 作为键没有意义。
ConcurrentHashMap 是线程安全的。 我相信不允许空键和值是确保它是线程安全的一部分。
我想 API 文档的以下片段给出了一个很好的提示:“此类在依赖其线程安全但不依赖其同步细节的程序中与 Hashtable 完全可互操作。”
他们可能只是想让ConcurrentHashMap
与Hashtable
完全兼容/可互换。 由于Hashtable
不允许空键和值..
我认为不允许空值是一个正确的选择。 在许多情况下,我们确实希望将具有空值的键放入并发映射中。 但是,通过使用 ConcurrentHashMap,我们无法做到这一点。 我建议即将发布的 JDK 版本可以支持这一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.