ConcurrentHashMap的JavaDoc说:
与
Hashtable
类似,但与HashMap
不同,此类不允许null
用作键或值。
我的问题:为什么?
第二个问题:为什么Hashtable不允许null?
我使用了很多HashMaps来存储数据。但是当改为ConcurrentHashMap时,由于NullPointerExceptions,我多次遇到麻烦。
答案 0 :(得分:204)
From the author of ConcurrentHashMap
himself (Doug Lea):
ConcurrentMaps中不允许空值的主要原因 (ConcurrentHashMaps,ConcurrentSkipListMaps)就是这种含糊之处 可能只是在非并发映射中几乎不能容忍 容纳。主要的一点是,如果
map.get(key)
返回null
,则为您 无法检测密钥是否明确映射到null
而密钥不是 映射。在非并发映射中,您可以通过以下方式检查map.contains(key)
,但在并发的地图中,地图可能已更改 在电话之间。
答案 1 :(得分:40)
我认为,至少在某种程度上,允许您将containsKey
和get
合并为一个调用。如果映射可以保存空值,则无法判断get
是否返回null,因为该值没有键,或者只是因为该值为null。
为什么会出现问题?因为没有安全的方法可以自己做到这一点。请使用以下代码:
if (m.containsKey(k)) {
return m.get(k);
} else {
throw new KeyNotPresentException();
}
由于m
是并发映射,因此可以在containsKey
和get
调用之间删除密钥k,从而导致此代码段返回表中从未出现的空值,而不是期望的KeyNotPresentException
。
通常你会通过同步来解决这个问题,但是使用并发的地图当然不会有效。因此,get
的签名必须更改,并且以向后兼容的方式执行此操作的唯一方法是阻止用户首先插入空值,并继续将其用作“key not”的占位符找到”。
答案 2 :(得分:4)
Josh Bloch设计HashMap
; Doug Lea设计了ConcurrentHashMap
。我希望这不是诽谤。实际上我认为问题是nulls经常需要包装,以便真正的null可以代表未初始化。如果客户端代码需要空值,那么它可以支付(通常很小的)包装空值的成本。
答案 3 :(得分:0)
ConcurrentHashMap是线程安全的。我相信不允许空键和值是确保它是线程安全的一部分。
答案 4 :(得分:0)
您无法在null上进行同步。
编辑:在这种情况下,这不正是原因。我最初认为有一些奇特的东西正在锁定对并发更新或者使用对象监视器来检测某些内容是否被修改,但在检查the source code后,我看错了 - 他们使用基于“段”的锁定在哈希的位掩码上。
在这种情况下,我怀疑他们是为了复制Hashtable而做的,我怀疑Hashtable是这样做的,因为在关系数据库世界中,null!= null,所以使用null作为键是没有意义的。
答案 5 :(得分:0)
我想以下API文档的片段提供了一个很好的提示: “这个类在依赖于线程安全但不依赖于其同步细节的程序中与Hashtable完全可互操作。”
他们可能只想让ConcurrentHashMap
与Hashtable
完全兼容/互换。因为Hashtable
不允许空键和值..
答案 6 :(得分:-1)
我认为不允许null值是正确的选择。 在许多情况下,我们确实希望将具有空值的键放入并行映射中。但是,通过使用ConcurrentHashMap,我们无法做到这一点。 我建议即将发布的JDK版本可以支持它。