为什么ConcurrentHashMap防止空键和值?
ConcurrentHashMap的JavaDoc说:
像
Hashtable
一样,但不像HashMap
,这个类不允许null
被用作键或值。
我的问题:为什么?
第二个问题:为什么不Hashtable允许null?
我用了很多HashMaps来存储数据。 但是当更改为ConcurrentHashMap时,由于NullPointerExceptions,我有几次陷入麻烦。
来自ConcurrentHashMap
本人(Doug Lea)的作者 :
ConcurrentMaps(ConcurrentHashMaps,ConcurrentSkipListMaps)中不允许空值的主要原因是在非并行映射中可能只能容忍的含糊不清是无法容纳的。 主要的是,如果
map.get(key)
返回null
,则不能检测到键是否显式映射到null
,而键是不映射的。 在非并发映射中,可以通过map.contains(key)
来检查,但是在并发映射中,映射可能在调用之间发生了变化。
我相信至less在一定程度上允许你将containsKey
合并成一个单独的调用。 如果映射可以包含空值,那么无法判断get
是否返回null,因为没有该值的键,或者仅仅是因为该值为null。
为什么这是一个问题? 因为没有安全的方法来做到这一点。 采取以下代码:
if (m.containsKey(k)) { return m.get(k); } else { throw new KeyNotPresentException(); }
由于m
是并发映射,因此可能会在containsKey
和get
调用之间删除键k,导致此片断返回从不在表中的null,而不是所需的KeyNotPresentException
。
通常情况下,你可以通过同步来解决这个问题,但是一个并发的映射当然是行不通的。 因此get
的签名必须改变,唯一的方法是以一种向后兼容的方式来阻止用户插入null值,并继续使用它作为“找不到密钥”的占位符。
Josh Blochdevise了HashMap
; Doug Leadevise了ConcurrentHashMap
。 我希望这不是诽谤。 其实我觉得问题是,空值往往需要包装,以便真正的null可以代表未初始化。 如果客户端代码需要空值,那么它可以支付(本来是小的)包装本身的成本。
你不能在null上同步。
编辑:这不完全是为什么在这种情况下。 我最初认为有一些奇怪的事情发生在locking并发更新或使用对象监视器来检测是否有被修改的东西,但是在检查源代码时 ,看起来我错了 – 它们使用基于散列的位掩码。
在这种情况下,我怀疑他们是否拷贝了Hashtable,而我怀疑Hashtable是这样做的,因为在关系数据库世界中,null!= null,所以使用null作为关键字没有任何意义。
ConcurrentHashMap是线程安全的。 我相信不允许null键和值是确保它是线程安全的一部分。
我猜API文档的以下代码给出了一个很好的提示:“这个类与Hashtable完全可以在依赖线程安全的程序中互操作,而不依赖于它的同步细节。
他们可能只是想让ConcurrentHashMap
完全兼容/可互换到Hashtable
。 而作为Hashtable
不允许空键和值..