数据结构之HashMap(容器)

HashMap

开始之前,先捋清楚思路,祭出我的三板斧:HashMap是什么?解决了什么?怎么实现的?

HashMap是什么?

基于哈希表实现的Map接口

HashMap解决了什么?

快速查找、插入和删除操作

HashMap是怎么实现的?

1.7 数组+链表(头插法)

1.8 数组+链表+红黑树(尾插法)

HashMap哈希冲突如何解决的?

上面我们讲到了hashmap的实现方式,数组+链表+红黑树(jdk1.8),哈希冲突实际主要是通过动态调整容量来进行的。

当key计算出的哈希值相同时,会被存储在同一个桶内,也就是数组中的同一个元素中,这个元素以链表或红黑树的方式存储.

HashMap扩容逻辑

核心参数:负载因子0.75,初始容量大小:16,每次扩容是2的倍数。

触发扩容分为两种情况,

第一种:数组长度大于 初始容量*负载因子(默认是12)

第二种:当链表大于8时,先判断数组长度是否超过了64,没超过则先对数组进行扩容,如果超过了则对链表进行树化操作。

扩容怎么执行的?

扩容通过resize()方法执行。

第一步,容量通常扩容为原来的两倍。

第二步,元素迁移:遍历原数组中的所有元素,重新计算每个元素在新数组中的位置。

(这里,1.8中对重新哈希做了一个优化,通过位运算即可确定新位置。无需再次哈希运算,更高效。)

何时树化?何时树转链表?

如果链表长度超过了8,且数组长度超过了64,便会在迁移时,将链表转换为红黑树。

反之,当红黑树节点数减少到6时,迁移时,红黑树便会退化成链表。

为什么1.8链表更改为尾插法?

1.7头插法时,并发扩容时,会引发死循环问题。

原因是数组迁移时,头插法会使迁移后的链表元素位置会发生反转(如旧链表顺序A→B→C,新链表变为C→B→A)。

1.8为解决这个问题由头插法改为尾插法,并维护局部变量表,来避免线程间干扰。

具体过程以两个线程T1和T2操作为例:
  1. ‌初始状态‌:T1和T2均指向链表头节点A,next指向B节点(如A→B)。‌
  2. ‌线程中断‌:T1执行扩容时,T2被挂起;T1完成扩容后,链表顺序变为B→A(因头插法反转)。‌
  3. ‌状态不一致‌:T2恢复执行时,仍基于旧状态(A→B),而实际链表已变为B→A。‌
  4. ‌形成循环‌:T2继续执行头插法操作,导致A和B节点相互引用(如A.next=B,B.next=A),形成闭环。‌
想了解红黑树,可看数据结构 之 红黑树 节点插入、删除时如何保证平衡
相关推荐
没有bug.的程序员2 分钟前
负载均衡的真正含义:从算法到架构的深度解析
java·jvm·算法·微服务·架构·负载均衡
yc_xym2 分钟前
[项目实践]言聚论坛(后端)
java·项目开发
谈笑也风生3 分钟前
经典算法题型之复数乘法(一)
数据结构·算法
多仔ヾ3 分钟前
Solon + EasyQuery + ElementPlus 实现后台管理系统之 08-权限认证优化
java
LambdaCat4 分钟前
如何用 Prompt 让 AI 主动发现设计问题
java·ai·ai编程
changlianzhifu14 分钟前
分账系统:从“资金管道“到“增长引擎“,重塑商业价值分配新范式
java·服务器·前端
木童6625 分钟前
Docker 容器操作(资源限制、数据卷容器、端口映射、容器互联)
运维·docker·容器
吃喝不愁霸王餐APP开发者6 分钟前
Java应用对接美团开放平台API时的HTTPS双向认证与证书管理实践
java·开发语言·https
宠..7 分钟前
QButtonGroup
java·服务器·开发语言·前端·数据库·c++·qt
码luffyliu7 分钟前
Go 语言并发编程:为何它能甩开 Java 等传统后端语言?
java·后端·golang·go