文章目录
最近看了黑马和刘老师的源码视频,故总结一篇HashMap的源码解析,文章分为上下两部分
HashMap集合简介
HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对。HashMap的实现是不同步的,这意味着他
不是线程安全的,它的key,value都可以为null
此外,HashMap中的映射不是有序的
- JDK1.8之前HashMap由数组+链表组成。数组是HshMap的主体,链表则是主要为了解决哈希冲突(两个对象调用的hashCode方法计算的哈希值一致导致计算的数组索引值相同(哈希寻址算法[hash&length-1]))而存在的(“拉链法”解决冲突)
- JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(或者红黑树的边界值,默认为8,且数组中桶的数量大于64,此时此索引位置上的所有数据改为使用红黑树存储)
图示:

hashMap扩容简介
- 初始数组长度为16,若链表节点数量小于等于8时,会将节点连接到链表的下一个位置
- 若某个链表上阈值大于8时,会根据cap(容量)进行判断,若此时数组容量小于64,则会进行数组扩容(从32扩容为64)
- 若链表节阈值大于8,且数组容量大于64,则链表进化为红黑树
问题:为什么要在数组长度大于64之后,链表才会进化为红黑树?
- 在数组比较小时如果出现红黑树结构,反而会降低效率,因为红黑树需要进行左旋右旋,变色这些操作来保持平衡,同时数组长度小于64时,搜索时间相对要快些,所以综上所述为了提高性能和减少搜索时间。
hashMap特点总结
- hashMap存取是无序
- 键和值位置都可以是null,但是键位置只能是一个null
- 键位置是唯一的,底层的数据结构是控制键的
- jdk1.8前数据结构是:链表+数组jdk1.8之后是:数组+链表+红黑树
- 阈值(边界值)>8并且数组长度大于64,才将链表转换成红黑树,变成红黑手的目的是为了高效的查询’
hashMap底层数据结构存储数据的过程
在了解源码之前,我们通过一个案例来说明hashMap底层数据结构存储数据的过程
我们先创建一个hashMap实例
HashMap<String, Integer> MapTest = new HashMap<>();
MapTest.put("a",1);
MapTest.put("b",2);
MapTest.put("c",3);
MapTest.put("a",44);
System.out.println(MapTest);
结果:
{
a=44, b=2, c=3}
存储过程分析
HashMap<String, Integer> MapTest = new HashMap<>();
1.在创建集合对象的时候,在jdk8前,在构造方法中创建一个长度为16的Entry[] table数组用来存储键值对数据,在jdk8以后不是在HashMap构造方法底层创建数组了,是在第一次调用put方法时创建的数组Node[] table
不在构造方法中创建的原因是:创建散列表需要耗费内存,而有些时候我们只是创建hashMap,并不向其中put元素
2.假设向哈希表中存储“a”-1数据,根据"a"调用String类中重写之后的hashCode方法计算出hash值,利用寻址算法(hash&(cap-1))cap为其容量,来寻找在哈希表中的索引,如果该索引空间没有数据,则直接将数据存储到数组中

3.向哈希表中存储数据"b"-2,假设"b"计算出的hashCode方法结合数组长度计算出的索引值也是3,如果此时数组空间不是null,此时底层会比较a和b的hash值是否一致,如果不一致,则在此空间上划出一个节点来存储键值对数据“b”-2,这种方式称为“拉链法”

4.假设向哈希表中存储数据"a"-44,根据“a”调用hashCode方法结合数组长度计算出索引值为3,此时数组空间不为null,此时比较后存储的数据留言和已经存在的数据的hash值是否相等,如果相等,此时发生hash碰撞,那么底层会调用“a”所属类String中的equals方法比较两个内容是否相等
- 相等:则将后添加的数据的value覆盖之前的value
- 不相等,此时出现hash冲突,继续向下和其他的数据key比较,如果都不相等,则划出一个节点存储数据
相同hash值的元素内容可能不同,例如"重地"和"通话"
System.out.println("重地".hashCode()=="通话".hashCode());
true
索引值->hash值->key值
为什么引入红黑树的进一步解答
JDK1.8以前HashMap的实现是数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。当HashMap中有大量的元素都存放在同一个桶中时,这个桶下有一条长长的链表,此时HashMap就相当于一个单链表,假如单链表有n个元素,遍历的时间复杂度就从O(1)退化成O(n),完全失去了它的优势,针对此种情况,JDK1.8中引入了红黑树(查找的时间复杂度为O(logn))来优化这种问题。
总结图

说明:
- size表示HashMap中K-V的实时数量,注意这个不等于数组的长度
- threshold(临界值)=capacity(容量)*loadFactory(加载因子),这个值是当前已占用数组长度的最大值。size超过这个临界值就重新resize扩容,扩容后的HashMap容量是之前容量的两倍
了解了hashMap的存储流程之后我们来看几道面试题加深印象

最低0.47元/天 解锁文章
6886





