作者:雷哥来源|Java面试真题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)Java语言中,ConcurrentHashMap和Hashtable是线程安全的集合,不允许key或value插入空值,但是HashMap允许插入空值的键或值。为什么?空值插入demo首先向HashMap中插入空值。实现代码如下:HashMapmap=newHashMap();//插入空值map.put(null,null);if(map.containsKey(null)){System.out.println("nullexists");}else{System.out.println("nulldoesnotexist");}上面程序的执行结果如下:从上面的结果我们可以看出HashMap允许key或者value插入空值。然后我们用同样的方法尝试往ConcurrentHashMap的key和value中插入空值。实现代码如下:编译阶段不报错。执行完上面的程序,结果如下:从上面的错误信息可以看出,使用ConcurrentHashMap是不能插入空值的,否则程序运行时会报空指针异常。PS:Hashtable的使用和ConcurrentHashMap类似,这里不再赘述。ConcurrentHashMap源码分析为了找到错误原因,我们尝试打开ConcurrentHashMap的源码一探究竟。打开ConcurrentHashMap添加元素。实现源码如下:从上面的源码可以看出,在add方法的第一句添加了一个判断:如果key值为null或者value值为null,则抛出异常NullPointerException直接地。不正常,这就是我们之前的程序报错的原因。探究最终原因通过上面的源码分析,我们似乎找到了ConcurrentHashMap不允许插入空值的原因。一句话概括就是:龟屁股“规矩”!不过这个理由并不能说服面试官,虽然源码是这样设计的,但是我们要思考的是这个设计背后更深层次的原因,为什么ConcurrentHashMap不允许插入null?而HashMap允许插入null?歧义所谓歧义是指意思不明确或意思不明确。我们假设ConcurrentHashMap允许插入null,那么这时候就会出现二义性问题,它的二义性有两个意思:该值不在集合中,所以返回null。该值为null,所以返回的是它原来的null值。可见这就是ConcurrentHashMap的二义性问题,那么HashMap为什么不怕二义性问题呢?FalsifiableHashMap说HashMap不怕二义性问题,为什么?这是因为HashMap的设计是为了单线程使用,所以如果在查询中发现了一个null值,我们可以通过hashMap.containsKey(key)方法来区分这个null值是存储在null中还是不存在根本不存在?这样二义性问题就可以得到解决,所以HashMap是不怕二义性问题的。UnfalsifiableConcurrentHashMap和ConcurrentHashMap不同,因为ConcurrentHashMap使用的场景是多线程的,所以它的情况比较复杂。我们假设ConcurrentHashMap可以存储空值。有这样的场景。现在一个线程A调用了concurrentHashMap.containsKey(key),我们期望返回的结果是false,但是我们调用concurrentHashMap.containsKey(key)之后,并没有返回结果之前,线程B调用了concurrentHashMap.put(key,null)存储一个null值,那么线程A最终返回true,这和我们之前预期的false完全不同。也就是说,多线程的情况是很复杂的。我们没有办法判断某一时刻返回的空值是空值还是根本不存在。即二义性问题无法证伪,所以ConcurrentHashMap将源码中的这种设计直接消除了key或value为null的二义性问题。ConcurrentHashMap设计者的回答关于ConcurrentHashMap不允许插入null值的问题,有人问过ConcurrentHashMap的作者DougLea,以下是他回复的邮件内容:ConcurrentMaps中不允许空值的主要原因是,ConcurrentSkash是,ConcurrentMaps(ConcurrentSkash)无法容纳在非并发映射中可能勉强可以容忍的歧义。最主要的是,如果map.get(key)返回null,则无法检测键是否显式映射到null与键是否未映射。在非并发地图中,您可以通过map.contains(key)检查这一点,但在并发地图中,地图可能在两次调用之间发生了变化。进一步离题:我个人认为允许在Maps(也包括Sets)中使用null是对程序的公开邀请,以包含在错误的时间中断之前一直未被发现的错误。(即使在非并发的Maps/Sets中是否允许null是JoshBloch和我长期不同意的围绕Collections的少数设计问题之一。)o在我的整个应用程序中检查空键和空值。在某个地方声明staticfinalObjectNULL=newObject();会不会更容易些?并用NULL替换地图使用中的所有空值?-Doug上面这封信的主要意思是,DougLea认为这样设计的主要原因是:不要容忍并发场景中的歧义!综上所述,在Java语言中,单线程下使用的HashMap等集合可以设置null值,而ConcurrentHashMap或Hashtable等并发集合则不允许为key或value设置null值。这是直接在JDK源代码级别实现的。这样设计的目的主要是为了防止并发场景下出现歧义。参考文档cnblogs.com/fangguangdexiaoyuer/p/12335921.html