HashMap的实现原理以及扩容机制

HashMap是Java编程语言中的一种哈希表数据结构,其实现了Map接口,是用于存储键值对(Key-Value)的集合。

HashMap 是一个以键值对形式存储数据的集合,在 HashMap 内部中,维护了一个存储数据的 Entry 数组,它的每一个 Entry 本质上就是一个单向链表。当发生哈希冲突时,HashMap 在 JDK7 时,采用(单向)链表的形式解决冲突;在 JDK8 时,采用(单向)链表 + 红黑树的形式解决冲突。

HashMap 的无参构造器默认只初始化加载因子0.75,也就是表示当前容量超过最大容量的75%,则会执行扩容操作。

加载因子0.75是根据泊松分布计算以及实践得出的结果(泊松分布主要是描述单位时间内随机事件发生次数的概率分布),是一个在性能和空间利用率之间的一个平衡数值。

简单来说,就是当加载因子越大,扩容时机就越晚,空间的利用率越高,但是哈希冲突会变多,导致查询和插入操作效率变低。

加载因子越小,则扩容时机越早,这样可以减少哈希冲突的概率,但是元素在数组中的分布会过于稀疏,导致浪费更多的存储空间。

当第一次使用 put() 方法添加元素时,方法内部会调用两个方法:

首先会调用hash()方法,它会先计算key的hash值,如果key为null则返回hash值为 0,hash值为0表示放在数组下标为0的位置;如果key不为null,则让key的hash值和右移16位后的hash值 两个数值进行异或运算,并返回结果。

最后就是调用put()的第二个方法,putVal()方法,这个方法内部首先会判断Entry数组是否为空,如果是为空的,则对数组进行扩容操作,扩容后的大小为16,阈值是12,这个阈值是根据加载因子和数组最大的容量计算出来的。

对空数组完成扩容后,下面则判断元素存储的下标位置,是否有其他元素存在(下标位置是通过数组长度-1与key的hash进行与运算,这个运算的结果就是要插入的下标位置),如果为null,则直接插入元素;如果不为null,则再判断插入元素的key和其他元素的key的内容是否相同,相同的话则新值替换旧值;如何不相同,则插入元素。

这个插入元素,在JDK7时,是使用头插法,也就是插在头节点之后,简而言之就是逆序插入;在JDK8时,是使用尾插法,也就是正序插入,尾插法虽然插入效率不如头插法,但是在并发情况下,可以避免单链表变成循环链表,造成的死循环问题。

然后是红黑树问题。因为是尾插法每次插入都需要从表头遍历到表尾,再加上单链表如果长度过长的话,会导致查询和插入效率变慢。所以是在 JDK8 时,引入了红黑树,红黑树可以在元素数量较多的情况下,让查询和插入效率会更高。

HashMap 中的单链表转为红黑树的条件是链表的元素个数大于8(就是从9开始),数组容量要大于等于64,两个条件必须全部满足,才会将那一条链表转为红黑树。

然后转为红黑树之后,并不会一直保持为红黑树,还是可能会退化成链表。这个分两种情况:

第一种,当执行了resize() 方法扩容时,如果树的节点个数(也就是元素个数)小于等于6时,会退化为链表。

第二种,当使用remove()方法删除元素时,底层会调用红黑树的removeTreeNode()方法,判断红黑树的 根节点 / 根节点的左孩子 / 根节点的左孩子 / 根节点的左孩子的左孩子 是否为null,只要有任意一个为null时,就会导致红黑树退化为单链表。

这个红黑树只适用于元素数量较多的情况下,因为红黑树会比单链表占用更多的空间开销。在元素数量较少的情况下,遍历单链表会更加的高效。

然后是数组扩容问题,因为HashMap的加载因子为0.75,所以当容量超过最大容量的75%时,就会实现自动扩容,这个扩容后的大小是原来旧容量的两倍。

这个扩容过程大致是这样的,首先会创建了一个是旧容量两倍的新数组,然后遍历旧数组中的每一个元素,将它们添加到新的数组中,这个重新插入的过程,会重新计算元素的哈希值,并根据新数组的容量大小确定元素在数组的索引位置。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值