的 JavaDoc 是ConcurrentHashMap
这样说的:
与 类似
Hashtable
但不同HashMap
的是,此类不允许用作null
键或值。
我的问题:为什么?
第二个问题:为什么Hashtable
不允许null?
我使用了很多 HashMaps 来存储数据。但是当更改为ConcurrentHashMap
NullPointerExceptions 时,我遇到了几次麻烦。
的 JavaDoc 是ConcurrentHashMap
这样说的:
与 类似
Hashtable
但不同HashMap
的是,此类不允许用作null
键或值。
我的问题:为什么?
第二个问题:为什么Hashtable
不允许null?
我使用了很多 HashMaps 来存储数据。但是当更改为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
andget
调用之间被删除,导致此代码段返回一个从未在表中出现的空值,而不是所需的KeyNotPresentException
.
通常你会通过同步来解决这个问题,但是使用并发地图当然不会工作。因此,必须更改 for 的签名get
,并且以向后兼容的方式做到这一点的唯一方法是防止用户首先插入空值,并继续将其用作“未找到密钥”的占位符。
乔什·布洛赫设计HashMap
;道格李设计ConcurrentHashMap
。我希望这不是诽谤。实际上我认为问题在于空值通常需要包装,以便真正的空值可以代表未初始化。如果客户端代码需要空值,那么它可以支付(诚然很小的)包装空值本身的成本。
您无法在 null 上进行同步。
编辑:这并不是在这种情况下的确切原因。我最初认为锁定事物以防止并发更新或以其他方式使用对象监视器来检测是否修改了某些东西,但在检查源代码时似乎我错了 - 他们使用基于“段”的“段”锁定哈希的位掩码。
在那种情况下,我怀疑他们这样做是为了复制 Hashtable,我怀疑 Hashtable 这样做是因为在关系数据库世界中,null != null,所以使用 null 作为键没有任何意义。
ConcurrentHashMap 是线程安全的。我相信不允许空键和值是确保它是线程安全的一部分。
我想 API 文档的以下片段给出了一个很好的提示:“在依赖线程安全但不依赖同步细节的程序中,此类与 Hashtable 完全可互操作。”
ConcurrentHashMap
他们可能只是想让Hashtable
. 而且 asHashtable
不允许空键和值..
我不认为不允许空值是一个正确的选择。在许多情况下,我们确实希望将具有空值的键放入并发映射中。但是,通过使用 ConcurrentHashMap,我们无法做到这一点。我建议即将到来的 JDK 版本可以支持这一点。