前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于ConcurrentHashMap的key和value不能为null的深层次原因

关于ConcurrentHashMap的key和value不能为null的深层次原因

作者头像
冬天里的懒猫
发布2020-09-01 15:08:48
5.9K0
发布2020-09-01 15:08:48
举报

前面分析ConcurrentHashMap的过程中可以发现,其要求key和value不能为空。实际上,不仅仅是ConcurrentHashMap,前面的HashTable,以及ConcurrentSkipListMap,这些并发的Map都不允许为空。在面试的过程中,不少大厂也会拿这个问题做为追问的问题之一。那么我们就来具体聊聊为什么不能为null的深层次的原因。

层次1:源码不支持

是的,实际上确实是在源码上就没用提供支持。在hashtable中,对value进行了非空校验,而key,如果为空则会出现NullPointerException。

代码语言:javascript
复制
if (value == null) {
    throw new NullPointerException();
}

// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();

key如果为空,则hashCode方法会出现空指针异常。 在ConcurrentHashMap中,和ConcurrentSkipListMap中,则分别进行了非空约束。 ConcurrentHashMap:

代码语言:javascript
复制
if (key == null || value == null) throw new NullPointerException();

ConcurrentSkipListMap:

代码语言:javascript
复制
public V put(K key, V value) {
    if (value == null)
        throw new NullPointerException();
    return doPut(key, value, false);
}

而这个doPut方法中:

代码语言:javascript
复制
if (key == null)
    throw new NullPointerException();

从上面可以看出,在代码中直接就杜绝了使用null的可能性,只有HashMap是支持null的,但是是在put为空的时候,hash方法对null做了特殊处理,为null的时候hash值位0。

层次2:null会带来二义性

之所以并发的ConcurrentHashMap不支持null的深层次的原因在于,null会带来难以容忍的二义性。我们可以看看Doug Lea对这个问题的描述。 Handling Null Values in ConcurrentHashMap

代码语言:javascript
复制
Tutika Chakravarthy wrote:
> Hi ,
> I would like to replace some Hashmaps in our
> application, which are prone to multi threading issues
> with ConCurrentHashMap.
> 
> Currently we keep null key and values in hashmap
> without any issues as HashMap allows them.
> 
> But ConcurrentHashMap does not allow any null key and
> values .
> 

Try to take Holger's advice. As mostly an aside though...

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.

Further digressing: I personally think that allowing
nulls in Maps (also Sets) is an open invitation for programs
to contain errors that remain undetected until
they break at just the wrong time. (Whether to allow nulls even
in non-concurrent Maps/Sets is one of the few design issues surrounding
Collections that Josh Bloch and I have long disagreed about.)

> 
> It is very difficult to check for null keys and values
> in my entire application .
> 

Would it be easier to declare somewhere
   static final Object NULL = new Object();
and replace all use of nulls in uses of maps with NULL?

-Doug

原作者认为,在ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps)上,不允许null值的出现的主要原因是他可能会在并发的情况下带来难以容忍的二义性。如果在HashMap等非并发容器中,你可以通过contains方法来判断,这个key是究竟不存在,还是本来就是null。但是在并发容器中,如果允许空值的存在的话,你就没法判断真正的情况。用作者的话说就是:在Maps或者Sets集合中允许null值的存在,就是公开邀请错误进入你的程序。而这些错误,只有在发生错误的情况下才能被发现。 我们可以对HashMap进行测试:

代码语言:javascript
复制
public static void main(String[] args) {
	HashMap map = new HashMap();
	map.put(null,1);
	System.out.println(map.containsKey(null));
	map.put(1,null);
	System.out.println(map.containsValue(null));
	System.out.println(map.get(null));
}

此时输出:

代码语言:javascript
复制
true
true
1

可见,在HashMap之中,我们可以很容易的通过contains方法来判断key或者value为null是否真的存在。 但是这个问题要是出现在ConurrentMaps中了,那么就可能会有问题了。试想一下,当我们首先从map中get某个key,由于map中这个key不存在,那么会返回null,这之后我们通过contains进行判断,此时如果有线程并发写入了一条value为null的值,那么contains的结果就为true。这样就会与真实的情况不一致了,这就是二义性。 因此我们也需要注意Doug 的观点:不管容器是否考虑了线程安全问题,都不应该允许null值的出现。他觉得在现有的某些集合里面允许了null值的出现,是集合的设计问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 层次1:源码不支持
  • 层次2:null会带来二义性
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档