为什么ConcurrentHashMap中key不允许为null?(美团)

  Doug Lea 是 Java 并发编程领域的知名专家,他曾经是 Java 并发包的主要设计者之一,也是 Java 并发编程的重要贡献者。对于 ConcurrentHashMap 不允许插入 null 值的问题,有人问过  Doug Lea,以下是他回复的邮件内容:

1 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.
2 
3 Further digressing: I personally think that allowingnulls in Maps (also Sets) is an open invitation for programsto contain errors that remain undetected untilthey break at just the wrong time. (Whether to allow nulls evenin non-concurrent Maps/Sets is one of the few design issues surroundingCollections that Josh Bloch and I have long disagreed about.)
4 
5 It is very difficult to check for null keys and valuesin my entire application .Would it be easier to declare somewherestatic final Object NULL = new Object();and replace all use of nulls in uses of maps with NULL?
View Code

(1)ambiguity  [ˌæmbɪˈɡjuːəti] n  歧义;模棱两可;
(2)barely tolerable勉强能容忍
(3)accommodate [əˈkɒmədeɪt] v 容纳;顺应;
(4)The main one is that 其中一个主要原因(问题)是
(5)detect  [dɪ'tekt] v 发现;查明;
(6)explicitly [ɪk'splɪsɪtli] adv 明确;显然;清楚地;直接地
(7)Further digressing此外,"Further digressing" 可以翻译成“进一步岔开话题”、“进一步离题”等意思,该短语表明作者在文章中暂时偏离了主题,不过很快会回到主题。
(8)"an open invitation for programs to contain errors" 直译是“程序包含错误的公开邀请”,意思是允许在程序中使用 null 值是一种可能导致程序包含错误的开放邀请。这是因为 null 值不表示具有意义的数据,如果在程序中使用 null 值,就需要在代码中处理这种情况,否则会导致程序崩溃或错误。如果程序中充斥着大量的 null 值,就会给程序员带来极大的困扰和错误的风险。
(9)最后一句话是作者在这里提出了一种解决方案,即定义一个全局的常量 NULL,将所有地图(maps)中的 null 值替换为这个常量,这样就可以避免使用 null 值时带来的问题。作者的意思是,这样做可能会使代码更加清晰和易于理解,并且有助于避免 null 值可能带来的错误。

直译

  ConcurrentMap(如ConcurrentHashMap、ConcurrentSkipListMap)不允许使用null值的主要原因是,在非并发的Map中(如HashMap)勉强可以容忍的歧义(二义性),在并发Map中是无法容忍的。其中一个主要问题是,如果 map.get(key) 返回 null,则无法检测键是否显式映射为null还是键未映射. 在非并发map中,您可以通过map.contains(key)来检查,但在并发map中,两次调用之间映射可能已经改变。
  此外:我个人认为在Maps(也包括Sets)中允许null是对程序包含错误的公开邀请,这些错误在错误时刻才会被检测出。(是否允许在非并发Maps/Sets中使用nulls是Josh Bloch和我在Collections设计问题上少数几个长期存在分歧的问题之一)
  在整个应用程序中检查null键和值非常困难。是否在某处声明static final Object NULL = new Object();并将map中所有使用null值的地方替换为NULL会更容易?

理解

  ConcurrentMap不允许使用null值的主要原因是,在非并发的Map中(如HashMap)勉强可以容忍的歧义(二义性),在并发Map中是无法容忍的。

  假如说,所有的Map都支持null的话,那么map.get(key)就可以返回null,但是,这时候就会存在一个不确定性,当你拿到null的时候,你是不知道他是因为本来就存了一个null进去还是说就是因为没找到而返回了null。

  在HashMap中,因为它的设计就是给单线程用的,所以当我们map.get(key)返回null的时候,我们是可以通过map.contains(key)检查来进行检测的,如果它返回true,则认为是存了一个null,否则就是因为没找到而返回了null。但是,像ConcurrentHashMap,它是为并发而生的,它是要用在并发场景中的,当我们map.get(key)返回null的时候,是没办法通过通过map.contains(key)检查来准确的检测,因为在检测过程中可能会被其他线程锁修改,而导致检测结果并不可靠。

  所以,为了让ConcurrentHashMap的语义更加准确,不存在二义性的问题,他就不支持null。

posted @ 2023-05-09 10:56  JustJavaIt  阅读(127)  评论(0编辑  收藏  举报