无意义的开头
- 在java集合容器里使用较多的有Map集合有HashMap和ConcurrentHashMap。
- 其中HashMap为非线程安全,只有在单线程内或并发读场景里使用。而ConcurrentHashMap为线程安全集合。
- 根据这两者使用场景的不同,可以解析出集合value赋值为null的两义性问题。
分析流程
-
先上图
-
上图里是HashMap集合中添加元素的方法源码,里面很明显没有对输入的key和value进行是否为null的判断。
-
上张图里是ConcurrentHashMap集合中添加元素的方法源码,在putVal()方法的第一行就对key和value进行了非null的判断。
-
那么这两个集合都是继承自Map,为什么在添加元素时一个需要验证是否为null,另一个不需要验证呢?
-
其实就要和它们的使用场景有关了:
- 在HashMap中一般多是使用在单线程或高并发读的场景里。那么如果HashMap.get(key)获取值null,这时就会有两种情况:一种是集合里没有对应的key,一种是集合里有key设置了一个null值。这里就存在两义性。然后可以使用HashMap.containsKey(key)方法来判断当前属于那种情况,这样就又能解决两义性。
- 在ConcurrentHashMap中一般多使用在多线程场景里。如果ConcurrentHashMap也允许value可以为null,那么当ConcurrentHashMap.get(key)获取值null也会存在上述的两义性,但却无法用containsKey(key)方法来解决两义性。因为线程A调用get(key)方法取值结果为null,然后在调用containsKey(key)前,线程B执行了put(key,null)操作,那么线程A再调用containsKey(key)验证是否存在key,那么这时会发现key是存在的,但其实对于线程A而已key是不存在的,就存在数据两义性问题。所以ConcurrentHashMap不允许value为null。
public static void main(String[] args) throws InterruptedException {
final Map hashMap = new HashMap();
String key = "hashKey";
new Thread(() -> {
final Object o = hashMap.get(key);
if (o == null) {
if (hashMap.containsKey(key)) {
System.out.println("hashMap 存在key为" + key + " value为null的键值");
} else {
System.out.println("hashMap 不存在key为" + key + " value为null的键值");
}
}
}).start();
Thread.sleep(100);
hashMap.put(key, null);
}
没有结尾硬加的结尾
- 其实这个问题之前我根本就没想到过,也是在之前看到其他博主的一篇文章后才来思考这个问题的。这道面试题我真不知道面试官想要的回答是什么
- 虚心学习,共同进步 -_-