HashTable,ConcurrentHashMap,ArrayList,LinkedListHashtMap等值对存储null以及二义性的深入研究
创始人
2024-05-29 03:13:27
0

本文是自己研究的,如果有任何疑问和补充,请您在评论区指正好嘛,我会立刻修改,以免误导他人.

如果您觉得本文质量不错,可以点个赞和关注一下好嘛.
如果您需要引用本文章的内容,请附加链接,谢谢.

问题描述:

为什么HashTable,ConcurrentHashMap不能保存null键和null值
为什么ArrayList ,LinkedList,甚至是HashMap都允许加入null键和值

博客概述

本文从设计思路到源码进行讲解.下面是概述
HashMap存在二义性 ----> ConcurrentMap,HashTable为什么不存在二义性 ---- > 二义性由 返回值 null ,不能代表元素的存在或者不存在引起 ----> 引申部分 : ArrayList,LinkedList 存在不存在二义性

HashMap能存储null-null,但是有二义性

首先要了解HashMap存在二义性.
HashMap能存储key == null, 也能存储 value == null

二义性是什么先看 get方法,get返回值有两种,一种是 key的值, 还有一种就是 null ,null 有两种含义,不存在该key 以及 存在该 key,但是存的值为 null,这就是二义性
下面来看方法

    public V get(Object key) {Node e;// 这里是关键哦,getNode后,会返回Node节点(里面保存了k-v),也就是说不存在的节点,返回的是null,没有异议吧.// 如果存在呢,也就是找到了一个e ,e != null ,e里面保存了k - v, 也就是返回 e.value 没问题吧,那么假如我value就是null 呢// 这里就出现了,如果没有这个Node,那么e == null,如果有Node,但是 value == null,返回的也是 null .//二义性就是,寻找Node的时候,找到了,其value == null,就返回null,找不到也返回null,有没有这个元素,都有可能返回 null,这就是二义性,即 null 代表两种状态,有歧义return (e = getNode(hash(key), key)) == null ? null : e.value;}

这里很简单,就是找到了返回元素,没找到返回null

    final Node getNode(int hash, Object key) {Node[] tab; Node first, e; int n; K k;if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode)first).getTreeNode(hash, key);do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;}

看到上面,你会觉得,二义性就是 通过 get() 方法的返回值,如果返回值是 null 无法判断出是否一定存在该元素.
但是,如果你觉得这是他设计的不好,或者是我讲错了.那你想想,这个"Bug"能解决吗,是否可以通过 contains(key) 方法,也就是说可以通过 contains 来判断是否存在该 key 行不行.

来看看contains方法,
这里可以解决二义性的,因为这里返回的是 boolean ,存在即存在,不存在即 false

    public boolean containsKey(Object key) {return getNode(hash(key), key) != null;}
    public boolean containsValue(Object value) {Node[] tab; V v;if ((tab = table) != null && size > 0) {for (int i = 0; i < tab.length; ++i) {for (Node e = tab[i]; e != null; e = e.next) {if ((v = e.value) == value ||(value != null && value.equals(v)))return true;}}}return false;}

有趣的理解:
当 get(key) 的结果为 null 时, 该 key 的存在或者不存在问题类似于薛定谔的猫(如果key存在,这个null就是key的值,如果key不存在,这个 null 只代表了不存在这个key),只有你 containsKey() 之后,才能判断存在这个 key

案例演示:

public class HashMapTest {public static void main(String[] args) {HashMap map = new HashMap<>();System.out.println("put方法,该方法返回null代表的是没有重复的key, 非null是返回的代表被覆盖的值,结果" + map.put("ggzx", null));System.out.println("get方法返回值,null 可以代表值为 null,也可能代表 不存在,我尝试获取,但结果  " + map.get("ggzx"));System.out.println(map.containsKey("ggzx"));System.out.println(map.containsValue(null));}
}
结果:
put方法,该方法返回null代表的是没有重复的key, 非null是返回的代表被覆盖的值,结果null
get方法返回值,null 可以代表值为 null,也可能代表 不存在,我尝试获取,但结果  null
true
true

看一看第二个输出:
如果你知道自己插入了数据,此时null代表了什么
如果你不知道自己插入了什么数据,这个时候你可以断定, null 是值 还是代表不存在.
如果你调用 cotainsKeys() ,能否能判断存在该key吗?

为什么有二义性还要搞

你已经了解了HashMap其实是存在二义性,但是也能够解决,在能解决的基础上,能保存 null-null,感觉也还行对吧.至少能表达一种状态,比如说存储 className --> null ,的情况,null 能代表不存在该 class

为什么HashTable和ConcurrentHashMap不能?

为啥,因为设计者都说了啊,他不想在并发过程中存在这个二义性问题!!!,不过这里我还想冒昧的看看,如果能保存,是否能够用containsKey()来解决

首先来看,Concurrent和HashTable 不能存储 null - null 的证据哇
ConcurrentHashMap#V putVal(K key, V value, boolean onlyIfAbsent)

	    final V putVal(K key, V value, boolean onlyIfAbsent) {// 对于ConcucrrentHashMap, 如果 key || value  == null,都会直接判处if (key == null || value == null) throw new NullPointerException();...本文讲null的存储的,又不讲源码,不给你看很多return null;
    public synchronized V put(K key, V value) {// 如果value == null ,直接抛出异常if (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry tab[] = table;// 如果 key == null,就抛出异常了,不过我也不知道为什么不检测,而需要运行时遇到异常抛出去int hash = key.hashCode();... 这部分不给你看了addEntry(hash, key, value, index);return null;}

好了,目前为止,你已经了解了,他们俩,都不能存储 key == null 或者 value == null.
不能存储 null ,返回值如果为 null ,肯定代表不存在了对吧,此时就没有二义性

看一看ArrayList吧

先看看ArrayList# E get(int index)
这里压根就不存在二义性哇,我传入一个索引,索引只有两个状态,无效的和有效的.结果不存在二义性,如果返回 null 呢?代表的就是存储的 null 呗,没有 null 代表不存在一说

    public E get(int index) {// 检查是否越界rangeCheck(index);// 直接返回return elementData(index);}

再看看ArrayList#boolean add(E e)
仔细看看返回的是啥,boolean,加入成功就是成功,失败就是失败,也不会返回null.
第二个重载方法,直接 void 了,还有啥二义性可言

	// 加入元素public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}// 重载方法public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size + 1);  // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;}

下面再看看 LinkedList 吧
存入null之前之后,都是返回的 null ,也无法判断,是否是存在key呢,还是不存在key呢

public class LinkedListTest {public static void main(String[] args) {LinkedList list = new LinkedList<>();System.out.println(list.peekLast());list.add(null);list.add(null);list.add(null);list.add(null);System.out.println(list.peekLast());}
}
结果:
null
null

文章结束

最主要的前面的部分,即HashTable,ConcurrentHashMap和HashMap,后面的可以用于拓展吧.
如果你有问题,您可以在评论区进行评论.
你都看到这里了,不打算点个赞和关注嘛

上一篇:【Java】JVM

下一篇:对跳表的深入理解

相关内容

热门资讯

《2025智慧生活科技好物换新... (来源:Runto洛图科技观研)7月15-16日,第二届 CIC 中国智慧生活大会在北京盛大开幕。大...
这一连续10年的纪录被终结! 连续10年都是40天的纪录被终结!今年“三伏”30天!“蝉从初伏噪,客向晚凉吟。”7月20日,今年“...
联合国难民署:逾1100万难民... 格隆汇7月19日|联合国难民署说,由于捐助国削减对外援助,多达1160万名难民面临失去人道援助的风险...
今明有雷阵雨伴短时暴雨,东南风... (来源:嘉兴日报)转自:嘉兴日报   今天多云到阴有时有阵雨或雷雨,雷雨时可伴有短时暴雨,明天多云,...
鹰眼预警:神通科技经营活动净现... Hehson财经上市公司研究院|财报鹰眼预警7月19日,神通科技发布2025年半年度报告。报告显示,...
税收抵免优惠、优化土地要素配置... 7月18日,国家发展改革委、财政部、自然资源部、商务部、中国人民银行、税务总局、国家外汇局七部门联合...
上半年龙岩市外贸进出口增长22... 本报讯 (记者 徐士媛 通讯员 林颖) 17日,记者从龙岩海关获悉,上半年龙岩市外贸进出口293.5...
驼鸟N斜桥镇庆云小学教育集团庆... (来源:南湖晚报)转自:南湖晚报  驼鸟N斜桥镇庆云小学教育集团庆云校区 402班 陈语烔 指导老师...
定胜糕 寄祝愿 (来源:嘉兴日报)转自:嘉兴日报   昨天中午,在南湖区凤桥镇“桃你糕兴”共富工坊内,负责人胡佳怡正...
N浙师大附属嘉兴市长虹实验学校... (来源:南湖晚报)转自:南湖晚报  N浙师大附属嘉兴市长虹实验学校 201班 徐梦汐 指导老师 张 ...