在Java编程中,ConcurrentHashMap和HashtaBLe等线程安全的集合不允许插入null值的key或value,而HashMap则允许,这种设计差异引发了许多开发者的困惑与报错,以下是详细解释:
ConcurrentHashMap 与 Null 值
1、源码分析:在ConcurrentHashMap的源码中,添加元素的方法put
在第一行就进行了判断,如果key值为null或者是value值为null,就直接抛出空指针异常NullPointerException
,这是导致插入null值时报错的直接原因。
2、设计原因:ConcurrentHashMap的设计者Doug Lea在邮件回复中提到,这样设计的主要目的是避免在并发场景下出现歧义问题,如果允许null值,那么当查询一个不存在的key时,返回null可能意味着该key确实不存在,也可能意味着该key存在但其对应的value是null,在单线程环境下,可以通过其他方法如containsKey
来区分这两种情况,但在多线程环境下,这种二义性问题无法解决。
3、二义性问题:在多线程环境下,如果一个线程查询某个key是否存在,另一个线程在该查询完成之前插入了一个null值,那么查询结果就会变得不可预测,为了避免这种情况,ConcurrentHashMap直接禁止了null值的插入。
4、性能考虑:虽然禁止null值插入可能会在某些情况下增加代码复杂度,但这也避免了在并发环境下可能出现的复杂逻辑和潜在的bug。
5、替代方案:如果确实需要在ConcurrentHashMap中存储null值,可以考虑使用特殊的标记对象,如new Object()
,或者使用支持null值的其他数据结构。
常见疑问解答
问题一:为什么HashMap允许插入null值而ConcurrentHashMap不允许?
答案:HashMap是单线程环境下使用的集合,它允许插入null值,因为在单线程环境下,即使查询到了null值,我们也可以通过hashMap.containsKey(key)
的方法来区分这个null值到底是存入的null还是压根不存在的null,从而解决了二义性问题,而ConcurrentHashMap是多线程环境下使用的集合,由于多线程环境的复杂性,我们无法判断某一个时刻返回的null值到底是值为null还是压根就不存在,也就是二义性问题不可被证伪,所以ConcurrentHashMap在源码中直接杜绝了key或value为null的歧义问题。
问题二:如何在ConcurrentHashMap中存储null值?
答案:由于ConcurrentHashMap不允许直接插入null值,因此需要采取一些替代方案来存储null值,一种常见的方法是使用特殊的标记对象来表示null值,可以使用new Object()
作为标记对象来表示null值,也可以考虑使用其他支持null值的数据结构来代替ConcurrentHashMap,需要注意的是,在使用这些替代方案时,需要确保它们能够满足程序的需求,并且不会引起其他问题。