美文网首页
为何 ConcurrentHashMap 不支持 null 键和

为何 ConcurrentHashMap 不支持 null 键和

作者: CHMAX | 来源:发表于2020-10-28 23:49 被阅读0次

区别

HashMap 同时支持 null 键和 null 值,而在 ConcurrentHashMap 中,却都不支持。通过浏览两者关键位置的代码,可以很快得出这个结论。

  1. [HashMap]

null 键做特殊处理,并默许 null 值的保存。

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  1. [ConcurrentHashMap]

明确不允许 null 键和 null 值的存在。

final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    ...
}

原因

但是,为何 ConcurrentHashMap 要选择不支持 null 键和 null 值呢?

首先,我们将问题拆分两个小问题,即为何不支持 null 键,以及为何不支持 null 值。

一、为何不支持 null 值?

其实,对于这个问题就有人就问过 ConcurrentHashMap 的作者 Doug Lea,以下是他的回答的邮件内容

Tutika Chakravarthy wrote:
> Hi ,
> I would like to replace some Hashmaps in our
> application, which are prone to multi threading issues
> with ConCurrentHashMap.
>
> Currently we keep null key and values in hashmap
> without any issues as HashMap allows them.
>
> But ConcurrentHashMap does not allow any null key and
> values .
>

Try to take Holger's advice. As mostly an aside though...

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.

Further digressing: I personally think that allowing
nulls in Maps (also Sets) is an open invitation for programs
to contain errors that remain undetected until
they break at just the wrong time. (Whether to allow nulls even
in non-concurrent Maps/Sets is one of the few design issues surrounding
Collections that Josh Bloch and I have long disagreed about.)

>
> It is very difficult to check for null keys and values
> in my entire application .
>

Would it be easier to declare somewhere
    static final Object NULL = new Object();
and replace all use of nulls in uses of maps with NULL?

-Doug

Doug Lea 如此设计最主要的原因是,不容忍在并发场景下出现歧义!!!

比如,通过调用 map.get(key) 获取值,但是返回结果为 null,如果是在并发场景下,是无法判断是键不存在,还是键对应的值为 null。在非并发的场景,可以通过 map.contains(key) 的方式检查,但是并发场景中,两次调用之间数据是会发生变化的。

可以参考以下代码,在并发场景下,即使 if 条件满足,键存在,但是并不能保证之后获取值时,键还存在,键是有可能被其它线程删除的。

if (map.contains(key)) {
    obj = map.get(key);
}
二、为何不支持 null 键?

从上面的 Doug Lea 的回复可以看出,他本人和 HashMap 的作者 Josh Bloch 在设计上是存在分歧的,他认为在 Maps(及 Sets)中允许出现 null,会导致一些未知的异常在特殊的情况下发生。

个人认为在实现上是可以支持的,但是作者的设计风格是想尽量避免不必要的隐藏异常。但是如果你确实需要在 ConcurrentHashMap 中使用 null 怎么办呢?

三、替代方案

可以使用一个特殊的静态空对象来代替 null

public static final Object NULL = new Object();

参考

相关文章

网友评论

      本文标题:为何 ConcurrentHashMap 不支持 null 键和

      本文链接:https://www.haomeiwen.com/subject/cilcvktx.html