24.HashMap的扩容机制

ps:没看太懂源码,不确定是否正确...

一、扩容条件

  当HashMap中元素的总个数超过(threshold)阈值(数组容量乘以负载因子)时,会触发扩容。默认情况下,(capacity)数组初始容量为16,(loadFactor)负载因子为0.75。


二、扩容过程

1.JDK 1.7的HashMap扩容机制:

  1. 首先,生成老数组长度两倍的新数组。
  2. 遍历老数组中桶中的每个元素。
  3. 根据key值重新计算每个元素的索引下标,将每个元素按照新的下标添加到新的数组中。
  4. 所有元素转移完成之后,新数组取代原数组,完成扩容。

(注意:在JDK 1.7中,HashMap在扩容时会采用头插法插入链表元素,这可能会导致在多线程环境下出现链表成环的问题。而在JDK1.8中,采用尾插法和链表重新链接解决此问题。)


2.JDK 1.8的HashMap扩容机制:

(前面两点与JDK1.7的一样)

  1. 首先,生成老数组长度两倍的新数组。
  2. 遍历老数组中桶中的每个元素。
  3. 如果桶节点还未形成链表,则计算出该元素在新数组中的索引位置,转移到新数组中。
  4. 如果桶节点已经形成了红黑树,则调用split方法。遍历这个红黑树中的所有节点,按照(该节点的哈希值与(&)老数组的长度值是否为0)来把原来的红黑树节点拆分成低位区和高位区两组树节点(此时应该是不符合红黑树的性质的)到新的数组中,然后根据节点个数,如果节点个数 <= 6,则要去树化操作,转化为链表,节点个数 >6,则树化操作为红黑树。 (按照(e.hash & bit) 是否 == 0,低位区头节点位置==原来位置,高位区头节点位置==原来位置+老数组长度)。
  5. 如果桶节点已经形成链表,则要遍历这个链表所有节点,一样是按照(该节点的哈希值与(&)老数组的长度值是否为0)来把原来的链表拆分为低位区和高位区两个链表重新分配到新数组中。(按照(e.hash & oldCap)是否 == 0,一样低位区头节点位置==原来位置,高位区头节点位置==原来位置+老数组长度)。
  6. 所有元素转移完成之后,新数组取代原数组,完成扩容。

注意:

  • 因为老数组的初始容量为2的次幂,扩容之后的长度是原来的两倍,即新数组的容量也是
    2的次幂。又因为元素的下标位置计算为tab[i = (n - 1) & hash],则下标 i 的二进制形式要么比原来左边新增一个0,或者新增一个1,则元素在新数组中的索引位置要么==原索引位置,要么==原索引位置+老数组长度)。
  • JDK1.8的HashMap默认内部的数组是null,即是没有实例化的。第一次调用put方法时,才会开始第一次初始化扩容,长度为16。即第一次扩容是初始化一个数组,而不是上面步骤,上面步骤是已经了第一次扩容的情况。

看源码:

 if ((e = oldTab[j]) != null) {oldTab[j] = null;//3.如果桶节点还未形成链表,则计算出该元素在新数组中的索引位置,转移到新数组中。if (e.next == null)newTab[e.hash & (newCap - 1)] = e;
//4.如果桶节点已经形成了红黑树,则调用split方法。else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//5.如果桶节点已经形成链表,则要遍历这个链表所有节点,一样是按照(该节点的哈希值与(&)老数组的长度值是否为0)来把原来的链表拆分为低位区和高位区两个链表重新分配到新数组中。else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}/*** Splits nodes in a tree bin into lower and upper tree bins,* or untreeifies if now too small. Called only from resize;* see above discussion about split bits and indices.** @param map the map* @param tab the table for recording bin heads* @param index the index of the table being split* @param bit the bit of hash to split on*/
//如果桶节点已经形成了红黑树,则调用split方法。遍历这个红黑树中的所有节点,按照(该节点的哈希值与(&)老数组的长度值是否为0)来把原来的红黑树节点拆分成低位区和高位区两组树节点(此时应该是不符合红黑树的性质的)到新的数组中,然后根据节点个数,如果节点个数 <= 6,则要去树化操作,转化为链表,节点个数 >6,则树化操作为红黑树。 final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {TreeNode<K,V> b = this;// Relink into lo and hi lists, preserving orderTreeNode<K,V> loHead = null, loTail = null;TreeNode<K,V> hiHead = null, hiTail = null;int lc = 0, hc = 0;for (TreeNode<K,V> e = b, next; e != null; e = next) {next = (TreeNode<K,V>)e.next;e.next = null;if ((e.hash & bit) == 0) {if ((e.prev = loTail) == null)loHead = e;elseloTail.next = e;loTail = e;++lc;}else {if ((e.prev = hiTail) == null)hiHead = e;elsehiTail.next = e;hiTail = e;++hc;}}if (loHead != null) {if (lc <= UNTREEIFY_THRESHOLD)tab[index] = loHead.untreeify(map);else {tab[index] = loHead;if (hiHead != null) // (else is already treeified)loHead.treeify(tab);}}if (hiHead != null) {if (hc <= UNTREEIFY_THRESHOLD)tab[index + bit] = hiHead.untreeify(map);else {tab[index + bit] = hiHead;if (loHead != null)hiHead.treeify(tab);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/13199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript函数声明

JS函数声明 JS中的方法,多称为函数,函数的声明语法和JAVA中有较大区别 语法1&#xff1a;function 函数名 (参数列表){函数体} 语法2&#xff1a;var 函数名 function (参数列表){函数体} 函数说明 函数没有权限控制符不用声明函数的返回值类型,需要返回在函数体中直接return即…

UBUNTU下指定执行文件运行时查找库的路径

在Ubuntu下&#xff0c;当指定执行文件时&#xff0c;程序运行时会查找库文件。通常情况下&#xff0c;程序会在系统默认的库文件路径中查找&#xff0c;例如/lib和/usr/lib。 如果需要程序在执行时查找特定路径下的库文件&#xff0c;可以通过以下方法实现&#xff1a; 设置环…

Gone框架介绍18 - redis 分布式缓存 和 分布式锁

gone是可以高效开发Web服务的Golang依赖注入框架 github地址&#xff1a;https://github.com/gone-io/gone 文档地址&#xff1a;https://goner.fun/zh/ 请帮忙在github上点个 ⭐️吧&#xff0c;这对我很重要 &#xff1b;万分感谢&#xff01;&#xff01; 文章目录 利用redi…

Python | Leetcode Python题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution:def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:# 设置 dummyNode 是这一类问题的一般做法dummy_node ListNode(-1)dummy_node.next headpre dummy_nodefor _ in range(left - 1):pre…

云计算第十八课

目录操作 移动 改名 批量改名&#xff0c;写脚本 mv [选项] … 源文件或目录… 目标文件或目录 单个文件 移动 或者改名 -f&#xff1a;强制覆盖&#xff0c;如果目标文件已经存在&#xff0c;则不询问&#xff0c;直接强制覆盖&#xff1b; -i&#xff1a;交互移动&#x…

零基础学Java第十四天之抽象类

抽象类和抽象类的深入 抽象类 1、理解 抽象类&#xff08;Abstract Class&#xff09;是面向对象编程中的一个重要概念&#xff0c;尤其在像Java、C#和C等编程语言中。抽象类是一种特殊的类&#xff0c;它不能被实例化&#xff08;即不能创建抽象类的对象&#xff09;&#x…

鼠标悬浮(hover)时显示提示框的效果

在Vue中&#xff0c;你可以使用多种方法来实现鼠标悬浮&#xff08;hover&#xff09;时显示提示框的效果。以下是一个简单的示例&#xff0c;它使用了Vue的指令&#xff08;directive&#xff09;和条件渲染&#xff08;conditional rendering&#xff09;来实现这个功能。 首…

关于FIFO Generator IP和XPM_FIFO在涉及位宽转换上的区别

在Xilinx FPGA中&#xff0c;要实现FIFO的功能时&#xff0c;大部分时候会使用两种方法&#xff1a; FIFO Generator IP核XPM_FIFO原语 FIFO Generator IP核的优点是有图形化界面&#xff0c;配置参数非常直观&#xff1b;缺点是参数一旦固定&#xff0c;想要更改的化就只能重…

一次tomcat闪退处理

双击tomcat目录下bin目录中startup.bat 在我的电脑上是一闪而过&#xff0c;不能正常地启动tomcat软件 以记事本打开startup.bat文件&#xff0c;在文件的结尾处加上pause 然后再双击该bat执行&#xff0c;此时窗口就不会关闭&#xff0c;并会将错误信息打印在提示框中 可能是…

英伟达发布 VILA 视觉语言模型,实现多图像推理、增强型上下文学习,性能超越 LLaVA-1.5

前言 近年来&#xff0c;大型语言模型 (LLM) 的发展取得了显著的成果&#xff0c;并逐渐应用于多模态领域&#xff0c;例如视觉语言模型 (VLM)。VLM 旨在将 LLM 的强大能力扩展到视觉领域&#xff0c;使其能够理解和处理图像和文本信息&#xff0c;并完成诸如视觉问答、图像描…

一看就会的AOP事务

文章目录 AOPAOP简介AOP简介和作用AOP的应用场景为什么要学习AOP AOP入门案例思路分析代码实现AOP中的核心概念 AOP工作流程AOP工作流程AOP核心概念在测试类中验证代理对象 AOP切入点表达式语法格式通配符书写技巧 AOP通知类型AOP通知分类AOP通知详解 AOP案例案例-测量业务层接…

Linux bc命令(bc指令)(基本计算器)(任意精度计算语言:支持浮点数运算、变量赋值和自定义函数等)

文章目录 bc命令文档英文中文 Linux bc 命令详解bc 命令的基本用法启动 bc 环境进行基本计算退出 bc bc 中的数学功能执行高级数学计算平方根和指数函数对数函数 处理精度问题 变量和数组变量赋值和使用数组的使用 创建和使用自定义函数 bc 命令的高级用法在脚本中使用 bc基本脚…

Google I/O 大会 | 精彩看点一览

作者 / 开发者关系和开源总监 Timothy Jordan 2024 年 Google I/O 大会于北京时间 5 月 15 日 1:00am 在加利福尼亚的山景城以 Google 主题演讲直播拉开序幕。随后&#xff0c;在北京时间 4:30am 举行开发者主题演讲。大家可前往回看 "Google 主题演讲" 以及 "开…

AIGC时代已至,你准备好抓住机遇了吗?

一、行业前景 AIGC&#xff0c;即人工智能生成内容&#xff0c;是近年来人工智能领域中发展迅猛的一个分支。随着大数据、云计算、机器学习等技术的不断进步&#xff0c;AIGC已经取得了显著的成果&#xff0c;并且在广告、游戏、自媒体、教育、电商等多个领域实现了广泛应用。…

AI写算法:支持向量机(SVM)

在Python中&#xff0c;我们可以使用scikit-learn库来实现支持向量机&#xff08;SVM&#xff09;。以下是一个简单的示例&#xff0c;演示如何使用scikit-learn的SVC类来训练一个SVM分类器&#xff0c;并使用它对一些数据进行预测。 python复制代码 # 导入必要的库 from skle…

图像中的attention及QKV机制解释

简单记录/推荐两篇博客&#xff0c;后续细化写一下&#xff1a; 图像中的各类 attention https://blog.csdn.net/weixin_44505185/article/details/127013204 Cross-attention的直观理解 首先理解&#xff0c;cross-attention 是两个不同向量间的相关计算&#xff0c;一般Q…

DolphinScheduler(海豚调度)- docker部署实战

1.官方文档 https://dolphinscheduler.apache.org/zh-cn/docs/3.2.1/guide/start/docker 2.docker环境安装 版本情况&#xff08;这个地方踩了不少坑&#xff09;&#xff1a;docker-26.1.2&#xff0c;docker-compose-v2.11.0。 具体可使用我上传的安装包&#xff0c;一键安…

leetcode题目55

跳跃游戏 中等 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1…

MT3037 新月轩就餐

思路&#xff1a; 此题每道菜的价钱相同&#xff0c;想最小化付的钱即求最小区间长度可以满足“品尝到所有名厨手艺”。 使用双端队列存储元素&#xff0c;队尾不断向后遍历&#xff1a;头->尾 如果队头队尾&#xff0c;则队头往右移一格&#xff0c;直到区间不同元素数m…

Docker部署MaxKB详细步骤(window系统)

上面章节已经实现了ollama李现部署llama3&#xff0c;并实现了一些简单的问答&#xff0c;但是问答的界面是在命令提示符中&#xff0c;交互很不友好&#xff0c;也不方便局域网其他用户访问&#xff0c;所以这节用docker部署MaxKB实现网页访问llama3&#xff0c;首先电脑上需要…