《集合》之hashmap
HashMap底层采用了数组+链表+红黑树
put操作
1)判断数组是否为空,为空进行初始化
2)不为空,计算 key 的 hash 值,通过(n - 1) & hash计算哈希槽;
3)查看哈希桶是否存在数据,没有数据 构造一个 Node节点 存放在 table[index] 中;
4)存在数据,hash冲突, 判断key是否相等,
相等,用新的value替换原数据;
若不相等,判断当前节点类型是不是树型节点,是树型节点,创造树型节点插入红黑树;
不是红黑树,创建普通Node加入链表;
链表长度 > 8,数组长度 >= 64,转红黑树。数组长度 < 64,扩容。
5)插入完成之后判断当前节点数是否大于阈值,大于,扩容为原数组的二倍
哈希函数
hash函数是先拿到 key 的hashcode,32位的值,高16位和低16位进行异或操作。
该函数也称为扰动函数,尽可能降低hash碰撞。
容量为什么 2^N
数组下标的计算方法是 (n - 1) 与 hash ,速度比取模运算快
索引值在容量中,不会超出数组长度
尽量把数据分配均匀,较少碰撞,让 HashMap 存取高效。
扩容
1)数组为空,或者数组的长度为0时,扩容
1)链表长度 > 8,数组长度 < 64,会引发扩容
1)默认负载因子0.75,数组中已存储的元素个数 > 数组长度75%,扩容
创建一个长度为原来数组长度 两倍 的新数组。
重新对原数组中的Entry对象进行哈希运算,确定在新数组中位置。
元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit
不需要重新计算hash,看原来的hash值新增 bit是 1还是0,0索引没变,1 索引变成“原索引+ n”
负载因子0.75
提高空间利用率和 减少查询成本的折中,主要是泊松分布
加载因子过高,例如为1,减少了空间开销,提高了空间利用率,但增加了查询时间成本;
加载因子过低,例如0.5,减少查询时间成本,但是空间利用率很低,同时提高了rehash 的次数。
为什么不直接红黑树,先链表再转红黑树
树节点的大小是链表节点大小的两倍,所以有足够的节点才用
转为树使得查找的速度更快,但是节点数少的时候,红黑树内存上的劣势会超过查找的优势,
在节点数比较多的时候,综合考虑,红黑树比链表要好。
链表长度是 8,不是9、10
理想情况下,哈希表中节点长度遵循泊松分布,8的时候概率就已经很小了,往后调整没有多大意义。
当hashCode离散性很好的时候,树型bin用到的概率非常小,因为数据均匀分布在每个bin中,几乎不会有bin中链表长度会达到阈值。
可以看到,链表长度达到8,概率为0.00000006,几乎不可能事件
1.7 死循环
扩容后链表中的节点在新的hash桶使用头插法插入,新的hash桶会倒置原hash桶中的单链表。
多个线程同时扩容的情况下,产生一个存在闭环的单链表,死循环。
1.8 采用尾插法,保证了链表的顺序与之前一致。
1.8中链表过长时会转换为红黑树,在转换为红黑树前,也是先根据尾插法生成新链表再进行转换。
JDK1.8死循环
红黑树成环,两个红黑树节点的父节点相互引用。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 解决跨域问题的这6种方案,真香!
· 分享4款.NET开源、免费、实用的商城系统
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库