154

的 JavaDoc 是ConcurrentHashMap这样说的:

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

我的问题:为什么?

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

我使用了很多 HashMaps 来存储数据。但是当更改为ConcurrentHashMapNullPointerExceptions 时,我遇到了几次麻烦。

4

7 回答 7

234

来自ConcurrentHashMap他自己的作者(Doug Lea)

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

于 2012-02-15T17:23:02.020 回答
51

我相信,至少在一定程度上,它允许您合并containsKeyget成为一个单一的呼叫。如果映射可以包含空值,则无法判断是否get返回空值是因为该值没有键,或者只是因为该值是空值。

为什么这是个问题?因为自己没有安全的方法来做到这一点。采取以下代码:

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

由于m是一个并发映射,键 k 可能会在containsKeyandget调用之间被删除,导致此代码段返回一个从未在表中出现的空值,而不是所需的KeyNotPresentException.

通常你会通过同步来解决这个问题,但是使用并发地图当然不会工作。因此,必须更改 for 的签名get,并且以向后兼容的方式做到这一点的唯一方法是防止用户首先插入空值,并继续将其用作“未找到密钥”的占位符。

于 2011-01-21T13:26:16.760 回答
4

乔什·布洛赫设计HashMap;道格李设计ConcurrentHashMap。我希望这不是诽谤。实际上我认为问题在于空值通常需要包装,以便真正的空值可以代表未初始化。如果客户端代码需要空值,那么它可以支付(诚然很小的)包装空值本身的成本。

于 2009-03-30T20:01:28.993 回答
3

您无法在 null 上进行同步。

编辑:这并不是在这种情况下的确切原因。我最初认为锁定事物以防止并发更新或以其他方式使用对象监视器来检测是否修改了某些东西,但在检查源代码时似乎我错了 - 他们使用基于“段”的“段”锁定哈希的位掩码。

在那种情况下,我怀疑他们这样做是为了复制 Hashtable,我怀疑 Hashtable 这样做是因为在关系数据库世界中,null != null,所以使用 null 作为键没有任何意义。

于 2009-03-30T19:18:16.180 回答
0

ConcurrentHashMap 是线程安全的。我相信不允许空键和值是确保它是线程安全的一部分。

于 2009-03-30T19:17:01.393 回答
0

我想 API 文档的以下片段给出了一个很好的提示:“在依赖线程安全但不依赖同步细节的程序中,此类与 Hashtable 完全可互操作。”

ConcurrentHashMap他们可能只是想让Hashtable. 而且 asHashtable不允许空键和值..

于 2009-03-30T19:29:01.707 回答
-3

我不认为不允许空值是一个正确的选择。在许多情况下,我们确实希望将具有空值的键放入并发映射中。但是,通过使用 ConcurrentHashMap,我们无法做到这一点。我建议即将到来的 JDK 版本可以支持这一点。

于 2019-01-08T09:43:05.213 回答