HashMap实现了Map接口,主要用于存储键值对数据。它是java开发中高频使用的一个API,接下来我们就通过源码来分析它是如何工作的,以及具有哪些特性。
首先来看看它是如何存储数据的,我们最常用的map.put(K,V)方法
我们调用put方法的时候其实内部调用的是putVal方法,这里一个值得关注的地方是hash(key)这个方法。我们先来看看它做了什么事?
hash(key),它调用了key的hashCode方法,然后进行了运算得到一个int值。这里延伸下。我们在用HashMap存储对象的时候Key值最好是String,int类型。因为它们已经实现了hashCode方法。如果我们要用其他对象去做Key值的话,必须是实现hashCode方法。
接下来我们看看putVal是如何实现的。
首先我们来看看这两行代码:
Node<K,V>[] tab;
if ((tab = table) == null || (n = tab.length) == 0)
注意table和tab是一个链表数组,这个是HashMap真正存储数据的容器。也是它决定着HashMap的特性。我们接着往下看。
第一、首先判断用于存放数据的链表数组是否为null,如果为null则初始化,tab = resize()。
第二、用数组链表(tab)的长度 &上我们要存储数据的Key的hash值,来确定我们要存储的数据将要存放于数组的位置,即确定tab的下标。
第三、判断要存放的位置是否已经存在值。如果没有值则直接将要存储的值存放链表数组。反之则进行下面的步骤。
第四
1、数组原本存在的节点(Node,最终存入map中的对象是以Node的形式存储在链表数组的)的Key值和即将要存储的Key完全相等,即将即将要存储的值覆盖原来的值。
2、两Key值不相等,情况。
如果存储的是TreeNode
遍历链表,比较Key值是否相等,根据Key的排序找到对应的地方插入即将要存储的值
不是TreeNode情况
遍历链表,并比较要存储的Key值是否和链表中已经存在的Node的Key值相等,相等则覆盖原来的Value。否则将即将要存储的值加入链表尾部。
好了至此,HashMap的数据存储就到这,我们来总结下,HashMap的存储过程。
首先通过Key的hashCode方法和HashMap底层的链表数组长度计算出要存储的键值对应该存放到数组的下标,然后再遍历下标下的链表,并判断Key的值是否存在。如果存在则覆盖原值,如果不存在则将其按照(TreeNode)一定规则插入链表。
通过源码我们也清楚知道,为什么在用其他对象做为Map的key的时候,我们要去覆写key对象的hashCode,equals,compareTo方法了。
接下来我们来看看HashMap的获取数据的过程map.get(Key);
第一、还是通过Key的hash值算出要取的值在链表数组的下标。如果下标下的链表为空,则直接返回空了。否则
第二、遍历链表,比较Key值,找到Key值,返回对应的Value
到此对应HashMap的简单分析就到此结束。回顾下前面讲到的LinkedList它的底层也是通过链表实现的,那么我们是不是可以把HashMap当作多个LinkedList呢?留给大家去思考了!!