为什么HashMap允许key和value为null,但是Concurrent HashMap不允许

首先,讨论value为什么不能为null

如果一个Map允许value为null,那么当调用map.get(key)返回null时,会出现二义性问题。可能会是:

  • 这个key不存在于map中;
  • 这个key存在于map中,并且其关联的val是null;

单线程环境下可以消除二义性。在单线程环境下使用HashMap,可以通过“先判断再取值”的组合操作来避免这种二义性问题,代码实现如下所示:

if (map.contains(key)) { //  该key存在于map中
    Object val = map.get(key); // 从map中取出key对应的val,如果val为null,说明“这个key是存在于map中,并且其关联的val是null”
} else {
    // 该key不存在于map中
}

多线程环境下无法消除二义性。在多线程环境下,“先判断再取值”的组合操作不具备原子性。在线程进行判断操作和取值操作之间,可能会有其它线程修改了这个map。具体来说,可能有以下情况:

1. 线程A调用了map.contains(key),返回true;

2. 线程B删除了这个key对应的键值对;

3. 线程A调用map.get(key),返回了null;

所以,线程A通过map.get(key)拿到返回值null,却依然无法区“这个key不存在”还是“这个key存在且其关联的val是null”

接下来,讨论key为什么不能null

HashMap的特殊处理。在HashMap中允许key为空,是因为HashMap对hash值的计算方式做了特殊处理,如下代码所示,key为null的键值在计算得到的哈希值是0,这个键值对会被固定放在数组的0号桶中。所以在HashMap中,key为null的键值对最多只有一个。

static final int hash(Object key) {
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

设计理念。在ConcurrentHashMap中不允许key为空,是因为ConcurrentHashMap的作者认为,允许将null作为key存入容器,容易隐藏业务逻辑中的bug(比如,会将原本应该有值但现在为null的key存入容器,却没报错)

规避特例。此外,如果允许key为空,会在代码中为null编写大量特殊分支(因为,null.hashCode()等方法会抛出空指针异常)。这会使代码变得冗余,而且会拖累并发容器的性能。

#HashMap##Java#
HashMap专栏 文章被收录于专栏

记录一些HashMap相关的面试题

全部评论

相关推荐

花环鞣:这个简历可以投微软谷歌苹果特斯拉了,多花点时间学下英语,准备出国,暑期把托福雅思考了
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务