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

时间:2022-12-22 05:31:30

The JavaDoc of ConcurrentHashMap says this:

ConcurrentHashMap的JavaDoc说:

Like Hashtable but unlike HashMap, this class does not allow null to be used as a key or value.

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

My question: why?

我的问题:为什么?

2nd question: why doesn't Hashtable allow null?

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

I've used a lot of HashMaps for storing data. But when changing to ConcurrentHashMap I got several times into trouble because of NullPointerExceptions.

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

6 个解决方案

#1


183  

From the author of ConcurrentHashMap himself (Doug Lea):

来自ConcurrentHashMap的作者本人(Doug Lea):

The main reason that nulls aren't allowed in ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that may be just barely tolerable in non-concurrent maps can't be accommodated. The main one is that if map.get(key) returns null, you can't detect whether the key explicitly maps to null vs the key isn't mapped. In a non-concurrent map, you can check this via map.contains(key), but in a concurrent one, the map might have changed between calls.

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

#2


29  

I believe it is, at least in part, to allow you to combine containsKey and get into a single call. If the map can hold nulls, there is no way to tell if get is returning a null because there was no key for that value, or just because the value was null.

我相信至少在某种程度上,允许你将containsKey组合起来并进入一个单独的调用。如果映射可以保存空值,则无法判断get是否返回null,因为该值没有键,或者只是因为该值为null。

Why is that a problem? Because there is no safe way to do that yourself. Take the following code:

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

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

Since m is a concurrent map, key k may be deleted between the containsKey and get calls, causing this snippet to return a null that was never in the table, rather than the desired KeyNotPresentException.

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

Normally you would solve that by synchronizing, but with a concurrent map that of course won't work. Hence the signature for get had to change, and the only way to do that in a backwards-compatible way was to prevent the user inserting null values in the first place, and continue using that as a placeholder for "key not found".

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

#3


3  

Josh Bloch designed HashMap; Doug Lea designed ConcurrentHashMap. I hope that isn't libelous. Actually I think the problem is that nulls often require wrapping so that the real null can stand for uninitialized. If client code requires nulls then it can pay the (admittedly small) cost of wrapping nulls itself.

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

#4


1  

You can't synchronize on a null.

您无法在null上进行同步。

Edit: This isn't exactly why in this case. I initially thought there was something fancy going on with locking things against concurrent updates or otherwise using the Object monitor to detect if something was modified, but upon examining the source code it appears I was wrong - they lock using a "segment" based on a bitmask of the hash.

编辑:这不是这种情况下的原因。我最初认为有一些花哨的事情可以锁定并发更新或者使用Object监视器来检测是否有东西被修改,但是在检查源代码时,我看错了 - 他们使用基于a的“段”锁定哈希的位掩码。

In that case, I suspect they did it to copy Hashtable, and I suspect Hashtable did it because in the relational database world, null != null, so using a null as a key has no meaning.

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

#5


0  

ConcurrentHashMap is thread-safe. I believe that not allowing null keys and values was a part of making sure that it is thread-safe.

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

#6


0  

I guess that the following snippet of the API documentation gives a good hint: "This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details."

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

They probably just wanted to make ConcurrentHashMap fully compatible/interchangeable to Hashtable. And as Hashtable does not allow null keys and values..

他们可能只是想让ConcurrentHashMap与Hashtable完全兼容/互换。并且由于Hashtable不允许null键和值..

#1


183  

From the author of ConcurrentHashMap himself (Doug Lea):

来自ConcurrentHashMap的作者本人(Doug Lea):

The main reason that nulls aren't allowed in ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that may be just barely tolerable in non-concurrent maps can't be accommodated. The main one is that if map.get(key) returns null, you can't detect whether the key explicitly maps to null vs the key isn't mapped. In a non-concurrent map, you can check this via map.contains(key), but in a concurrent one, the map might have changed between calls.

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

#2


29  

I believe it is, at least in part, to allow you to combine containsKey and get into a single call. If the map can hold nulls, there is no way to tell if get is returning a null because there was no key for that value, or just because the value was null.

我相信至少在某种程度上,允许你将containsKey组合起来并进入一个单独的调用。如果映射可以保存空值,则无法判断get是否返回null,因为该值没有键,或者只是因为该值为null。

Why is that a problem? Because there is no safe way to do that yourself. Take the following code:

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

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

Since m is a concurrent map, key k may be deleted between the containsKey and get calls, causing this snippet to return a null that was never in the table, rather than the desired KeyNotPresentException.

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

Normally you would solve that by synchronizing, but with a concurrent map that of course won't work. Hence the signature for get had to change, and the only way to do that in a backwards-compatible way was to prevent the user inserting null values in the first place, and continue using that as a placeholder for "key not found".

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

#3


3  

Josh Bloch designed HashMap; Doug Lea designed ConcurrentHashMap. I hope that isn't libelous. Actually I think the problem is that nulls often require wrapping so that the real null can stand for uninitialized. If client code requires nulls then it can pay the (admittedly small) cost of wrapping nulls itself.

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

#4


1  

You can't synchronize on a null.

您无法在null上进行同步。

Edit: This isn't exactly why in this case. I initially thought there was something fancy going on with locking things against concurrent updates or otherwise using the Object monitor to detect if something was modified, but upon examining the source code it appears I was wrong - they lock using a "segment" based on a bitmask of the hash.

编辑:这不是这种情况下的原因。我最初认为有一些花哨的事情可以锁定并发更新或者使用Object监视器来检测是否有东西被修改,但是在检查源代码时,我看错了 - 他们使用基于a的“段”锁定哈希的位掩码。

In that case, I suspect they did it to copy Hashtable, and I suspect Hashtable did it because in the relational database world, null != null, so using a null as a key has no meaning.

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

#5


0  

ConcurrentHashMap is thread-safe. I believe that not allowing null keys and values was a part of making sure that it is thread-safe.

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

#6


0  

I guess that the following snippet of the API documentation gives a good hint: "This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details."

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

They probably just wanted to make ConcurrentHashMap fully compatible/interchangeable to Hashtable. And as Hashtable does not allow null keys and values..

他们可能只是想让ConcurrentHashMap与Hashtable完全兼容/互换。并且由于Hashtable不允许null键和值..