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

Mar*_*cel 136 java concurrenthashmap

ConcurrentHashMap的JavaDoc 说:

类似Hashtable但不同HashMap,这个类就不会允许null用作键或值.

我的问题:为什么?

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

我使用了很多HashMaps来存储数据.但是当更改为ConcurrentHashMap时,由于NullPointerExceptions,我遇到了几次麻烦.

Bru*_*uno 212

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

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

  • 谢谢,但是把null作为关键呢? (6认同)
  • @benez `Optional` 是 Java 8 的一项功能,当时不可用(Java 5)。你现在可以使用`Optional`s,确实。 (5认同)
  • 为什么不在内部使用`Optional`作为值 (2认同)

Ali*_*ell 42

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

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

if (m.containsKey(k)) {
   return m.get(k);
} else {
   throw new KeyNotPresentException();
}
Run Code Online (Sandbox Code Playgroud)

由于m是并发映射,因此可以在containsKeyget调用之间删除密钥k ,从而导致此代码段返回从未在表中的null,而不是所需的KeyNotPresentException.

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


Tom*_*ine 5

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