為什么ConcurrentHashMap不允許插入Null值?
在Java語言中,給ConcurrentHashMap和Hashtable這些線程安全的集合中的Key或者Value插入 null(空) 值的會(huì)報(bào)空指針異常,但是單線程操作的HashMap又允許 Key 或者 Value 插入 null(空) 值。這到底是為什么呢?
1.探尋源碼
為了找到原因,我們先來看這樣一段源碼片段,打開ConcurrentHashMap的putVal()方法,源碼中第一句就非常明確地做了判斷,如果 Key 或者 Value 為 null(空) 值,就直接拋出空指針異常。
我們?cè)谠创a中似乎已經(jīng)找到了原因,你可以這樣回答面試官,說JDK源碼就是這么規(guī)定的。然而,這個(gè)原因是不能說服面試官的,雖然,源碼是這樣設(shè)計(jì)的,我們要思考的是,這樣設(shè)計(jì)背后更深層次的原因。
那到底為什么ConcurrentHashMap不允許插入 null (空)值,HashMap又允許插入呢?
2.歧義問題
因?yàn)榻oConcurrentHashMap中插入 null (空)值會(huì)存在歧義。我們可以假設(shè)ConcurrentHashMap允許插入 null(空) 值,那么,我們?nèi)≈档臅r(shí)候會(huì)出現(xiàn)兩種結(jié)果:
1)值沒有在集合中,所以返回的結(jié)果就是 null (空);
2)值就是 null(空),所以返回的結(jié)果就是它原本的 null(空) 值。
這就產(chǎn)生了歧義問題。
那HashMap允許插入 null(空) 值,難道它就不擔(dān)心出現(xiàn)歧義嗎?這是因?yàn)镠ashMap的設(shè)計(jì)是給單線程使用的,所以如果取到 null(空) 值,我們可以通過HashMap的 containsKey(key)方 法來區(qū)分這個(gè) null(空) 值到底是插入值是 null(空),還是本就沒有才返回的 null(空) 值。
而 ConcurrentHashMap 就不一樣了,因?yàn)?ConcurrentHashMap 是在多線程場景下使用的,它的情況更加復(fù)雜。
舉個(gè)例子,現(xiàn)在有線程T1調(diào)用了 ConcurrentHashMap 的 containsKey(key) 方法,我們期望返回的結(jié)果是false,也就是說,T1并沒有往ConcurrentHashMap中 put null(空)值。
但是,恰恰出了個(gè)意外,在線程T1還沒有得到返回結(jié)果之前,線程T2又調(diào)用了ConcurrentHashMap 的 put() 方法,插入了一個(gè)Key,并且存入的Value是 null(空) 值。那么,線程T1 最終得到的返回結(jié)果就變成 true 了。
顯然,這個(gè)結(jié)果和我們之前期望的 false 完全不一致。
也就是說,在多線程的復(fù)雜情況下,我們多線程的復(fù)雜情況下,到底是插入的 null(空) 值,還是本就沒有才返回的 null(空) 值。也就是說,產(chǎn)生的歧義不能被 證 偽,
3.作者回復(fù)
對(duì)于 ConcurrentHashMap 不允許插入 null 值的問題,有人問過 ConcurrentHashMap 的作者 Doug Lea,以下是他回復(fù)的郵件內(nèi)容:
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 認(rèn)為這樣設(shè)計(jì)最主要的原因是:不容忍在并發(fā)場景下出現(xiàn)歧義!
4.總結(jié)
ConcurrentHashMap在源碼中加入不允許插入 null (空) 值的設(shè)計(jì),主要目的是為了防止并發(fā)場景下的歧義問題。
以上就是我對(duì)關(guān)于ConcurrentHashMap為什么不允許插入 null (空) 值的解答,聽懂的小伙伴,請(qǐng)關(guān)注點(diǎn)個(gè)贊,下次不迷路。