代码如下
/** * 测试ConcurrentHashMap null键和null值的问题 * @return */ @RequestMapping(value = "/get_nacos") public String getNacos(){ ConcurrentHashMap<String,String> map =new ConcurrentHashMap<>(1); map.put("testKey", null); String nullValue = map.get("sss"); return nullValue; } }
其中在执行put操作时,会报错,信息如下:
java.lang.NullPointerException: null at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:1.8.0_162] at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006) ~[na:1.8.0_162] at com.gabriel.stage.controller.TestController.getNacos(TestController.java:44) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_162] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_162] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_162] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_162] at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
那么这里就有一个疑问了,为什么HashMap能存null键和null值,ConcurrentHashMap就不能存null键和null值了呢,并且还会出现空指针异常
个人理解如下:
ConrrentHashMap 是一个用于多线程并发场景下的并发容器(Map),也就是在多线程环境下执行增删改查方法要保证线程安全性,
例如containsKey()方法中
public boolean containsKey(Object key) { return get(key) != null; }
该方法会调用get方法去查询key值是否存在,此时如果key值是我们手动存进去的,这个时候就会在代码语义上有区别,无法区分是真的没有该key 还是我们存储的key,
并且在调用get()方法时,可能会出现别的线程修改为null键和null值的情况,这中情况在并发场景下会产生歧义;
HashMap本身是一个线程不安全的容器(Map),也就是使用场景局限于单线程环境,因此存储null键null值 通常不会有什么问题,
HashMap将存储的null键存储到数组的第1个元素;