HashMap源码:
612行:hash()方法计算了key的值
339行:当key为null时,计算出的hash值为0,value放置在第0个桶上
ConcurrentHashMap源码:
1006行:没有像HashMap一样先计算hash
1011行:先进行了判断key和value是否为null
为什么ConcurrentHashMap需要加空值校验呢?
因为存在二义性问题且ConcurrentHashMap没法解决
二义性问题
测试代码
代码分析
22行:获取test的value
23行:containsKey判断是否有test
24行:增加test和null值
25行:再次获取test的value
26行:containsKey再次判断是否有test
测试结果
结果分析
get方法获取到的value的结果都为null。所以当我们用get方法获取到一个value为null的时候,这里会产生二义性:
- 可能没有test这个key
- 可能有test这个key,只不过value为null
HashMap如何解决二义性问题
containsKey方法的结果一个为false一个为true,可以通过这个方法来区分上面说道的二义性问题
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
- 如果存在key为null的元素(key=null对应的hash值=0),getNode获取到值不为null;
- 如果不存在key为null的元素,此时hash值=0对应的下标元素为null,即getNode获取到的值为null;
ConcurrentHashMap为什么不能解决二义性问题
因为ConcurrentHashMap是线程安全的,一般使用在并发环境下,你一开始get方法获取到null之后,再去调用containsKey方法,没法确保get方法和containsKey方法之间,没有别的线程来捣乱,刚好把你要查询的对象设置了进去或者删除掉了。