HashMap扩容原理
HashMap扩容原理#
在添加元素或初始化的时候需要调用resize
方法进行扩容,第一次添加数据初始化数组长度为16
,以后每次每次扩容都是达到了扩容阈值(数组长度*0.75)。
每次扩容的时候,都是扩容之前容量的2
倍。
扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中。
- 没有hash冲突的节点,则直接使用
e.hash&(newCap-1)
计算新数组的索引位置。 - 如果是红黑树,走红黑树的添加。
- 如果是链表,则需要遍历链表,可能需要拆分链表,判断
e.hash&oldCap
是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上。
JDK1.7多线程扩容死循环#
在JDK1.7的hashmap中在数组进行扩容的时候,因为链表是头插法
,在进行数据迁移的过程中,有可能导致死循环。
比如说,现在有两个线程
线程一:读取到当前的hashmap数据,数据中一个链表,在准备扩容时,线程二介入。
线程二:也读取hashmap,直接进行扩容。因为是头插法,链表的顺序会进行颠倒过来。比如原来的顺序是AB,扩容后的顺序是BA,线程二执行结束。
线程一:继续执行的时候就会出现死循环的问题。
线程一先将A移入新的链表,再将B插入到链头,由于另外一个线程的原因,B的next指向了A,所以B->A->B,形成循环。
当然,JDK8将扩容算法做了调整,不再将元素加入链表头(而是保持与扩容前一样的顺序),尾插法
,就避免了JDK1.7中死循环的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App