HashMap 底层原理解析

目录

一、HashMap的数据结构

1、红黑树介绍:

2、树化的两个条件

3、链表化的条件

二、HashMap的索引计算

三、HashMap的Put方法实现流程

四、HashMap的加载因子

五、HashMap的并发数据

六、HashMap的Key


一、HashMap的数据结构

HashMap未树化前的数据结构:

放入的元素经过两次hash后,取模放入hashmap对应位置。

java1.7:数组+链表

java1.8:数组+ (链表 | 红黑树)

解析1.8版本:

1、红黑树介绍:

img

(1)hashmap中的树化主要是为了预防Dos攻击,防止超长的链表影响查询性能,树化也是偶然的结果,因为一般情况下,某个数组元素的链表是不会超过8个元素。

(2)hash表的查询时间复杂度为O(1),而红黑树查询的时间复杂度为O(log2 N),TreeNode占用的空间也比普通Node大,如非必要,尽量使用链表。

(3)hash值是随机的,在hash表内部服从泊松分布,负载因子0.75的情况下,长度超过8的链表出现的几率是0.00000006,选择8就是让树化几率足够小。

2、树化的两个条件

树化介绍:

hashmap中每个数组元素对应一个链表,当链表长度在非正常情况下超过8(树化阀值)时,链表树化为红黑树以便查询效率提升。

(1)链表长度超过树化阀值:8。

(2)数组容量大于等于64。

两者缺一不可。

Hashmap的数组扩容机制:

链表长度超过8时,若数组长度小于等于64会先进行扩容,最终再看要不要树化。

Hashmap默认的数组容量是16,当出现链表长度超过8时,容量变为原来的2倍,(取2倍是因为在2倍的基础上做了些优化),扩容后重新计算hash值分布会更发散,当容量扩容到64以上时,对应的链表长超过8的也会发生树化。

如果数组扩容后大于等于64个元素,某个元素结点的链表长度仍大于8则会发生树化。

3、链表化的条件

链表化介绍:

该过程刚好与树化相反。

(1)退化情况1:在扩容如果拆分树时,树元素个数<=6,则会退化为链表。

(2)退化情况2:remove树节点时,若root、root.left、root.right、root.left.left有一个为NULL也会退化为链表。

二、HashMap的索引计算

1、计算对象的hashCode(),再调用Hashmap的hash()方法进行二次哈希,最后 & (Capacity - 1)得到索引。

这里提一下: 取2的模运算为:x%2 =  x & (数组元素长度 - 1) 

由于&运算效率高于取模运算,所以采用后者。注意: 只要2的mode才满足该等式。

2、二次hash()是为了综合高位数据,让hash后的分布更均匀。

3、计算索引时,如果数组的容量是2的n次幂时,可以使用位与运算代替取模,效率更高;扩容时hash & oldCap = 0的元素留在原来的位置,否则新位置=旧位置+oldCap

4、1~3都是在数组容量为2的n次幂时的有化,例如HashTable的容量就不是2的n次幂,并不是说哪种设计更优,设计者通过综合各方面因素后,最终选取2的n次幂作为容量。

三、HashMap的Put方法实现流程

(1)HashMap是懒惰创建数组的,首次使用才创建数组。

(2)计算索引(桶下标)

(3)如果桶下标还没被人暂用,创建Node占位返回。

(4)如果桶下标已被人占

情况1:已经是TreeNode走红黑树的添加或更新逻辑;

情况2:是普通Node, 走链表的添加或更新逻辑,如果链表长度超过树化阀值,走树化逻辑

(5)返回前检测容量是否超过阀值,一旦超过进行扩容。

四、HashMap的加载因子

加载因子默认为0.75f。

1、在空间占用与时间查询之间取得一个较好的平衡。

2、大于这个值空间节省了,但链表就会比较影响性能。

3、小于这个值,冲突减小了,但扩容就会更频繁,空间占用多。

五、HashMap的并发数据

多线程下:

1.7版本会出先扩容死链

1.7版本和1.8版本都会出现数据错乱

六、HashMap的Key

1、HashMap的key可以为null,但Map的其他实现却不能。

2、作为Key的对象,必须实现hashCode和equals,并且key内容不能修改(不可变)。

(equals的两个元素hashCode一定相等,hashCode相等的元素不一定是同一个元素)

补充:

字符串的hashCode()设计

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值