这是我在面试的时候碰到面试官问到的一个问题;
其实这个问题问的是比较刁钻的,因为要从源码设计的角度去回答这个问题,
首先,允许存在null键值的map集合为HashMap,那么为什么HashTable和ConcurrentMap这两个集合不允许null值呢;
这里上作者的原话:
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.
大致意思是:在单线程环境中,不会存在一个线程操作该 HashMap 时,其他的线程将该 HashMap 修改的情况,可以通过 contains(key)来做判断是否存在这个键值对,从而做相应的处理; 而在多线程环境下,可能会存在多个线程同时修改键值对的情况,这时是无法通过contains(key)来判断键值对是否存在的,这会带来一个二义性的问题,Doug Lea说二义性是多线程中不能容忍的!
也就是说作者在设计HashMap集合之初,就完全没有考虑他的安全性,默认的就是单线程执行的;当key为null进行hash运算的时候采用三目运算,hash值直接返回0;
源码如下:
而HashTable和ConcurrentMap作为安全的集合,在设计之初是要考虑到线程安全的问题,也就是说有多个线程同时对键值进行修改,那么此时在 使用contains(key)方法的时候就会出现一个歧义,到底是key为null呢,还是说根本不存在null这个key呢,这时候就产生了歧义,所以安全的集合键值都是不能为null的。