为什么ConcurrentHashMap会阻止空键和值?

问题

JavaDoc ofConcurrentHashMap认为:

与Hashtable类似,但与HashMap不同,此类不允许将null用作键或值。

我的问题:为什么?

第二个问题:为什么Hashtable不允许null?

我使用了很多HashMaps来存储数据。但是当改为ConcurrentHashMap时,由于NullPointerExceptions,我多次陷入困境。


#1 热门回答(184 赞)

来自ConcurrentHashMaphimself(Doug Lea)的作者:

ConcurrentMaps(ConcurrentHashMaps,ConcurrentSkipListMaps)中不允许空值的主要原因是,无法容纳在非并发映射中几乎不能容忍的模糊性。主要的一点是,如果map.get(key)返回null,则无法检测密钥是否显式映射到null而密钥未映射。在非并发映射中,你可以通过map.contains(key)进行检查,但在并发映射中,映射可能在调用之间发生了变化。


#2 热门回答(29 赞)

我相信,至少在某种程度上,允许你将containsKeyget合并为一个电话。如果映射可以保存空值,则无法判断ifget是否返回null,因为该值没有键,或者只是因为该值为null。

为什么这是一个问题?因为没有安全的方法可以自己做。请使用以下代码:

if (m.containsKey(k)) {
   return m.get(k);
} else {
   throw new KeyNotPresentException();
}

由于m是并发映射,因此可以在containsKeygetcalls之间删除密钥k,导致此代码段返回从未在表中的空值,而不是期望的KeyNotPresentException

通常你会通过同步来解决这个问题,但是使用并发映射当然不会起作用。因此,get的签名需要更改,并且以向后兼容的方式执行此操作的唯一方法是阻止用户首先插入空值,并继续将其用作"未找到密钥"的占位符。


#3 热门回答(4 赞)

Josh Bloch designedHashMap; Doug Lea设计的ConcurrentHashMap。我希望这不是诽谤。实际上我认为问题是nulls经常需要包装,以便真正的null可以代表未初始化。如果客户端代码需要空值,那么它可以支付(通常很小的)包装空值本身的成本。