HashMap~

news/2024/5/10 6:53:58/文章来源:https://blog.csdn.net/m0_64365419/article/details/129169599

HashMap:

HashMap是面试中经常被问到的一个内容,以下两个经常被问到的问题,

Question1:底层数据结构,1.7和1.8有何不同?

答:1.7数组+链表,1.8数组+(链表|红黑树)

Question2:为何要用红黑树,为何一上来不树化,树化阈值为何是8,何时会转化,何时会退化为链表?

答: 红黑树用来避免 Dos 攻击防止链表超长时性能下降,树化应当是偶然情况

hash 表的查找,更新的时间复杂度是 0(1),而红黑树的查找,更新的时间复杂度是 0(log2^n),TreeNode 占用空间也比普通 Node 的大,如非必要,尽量还是使用链表

hash 值如果足够随机,则在 hash 表内按泊松分布,在负载因子 0.75 的情况下,长度超过 8 的链表出现概率是0.00000006,选择8就是为了让树化几率足够小

树化两个条件:链表长度超过树化值;数组容量>=64

退化情况1:在扩容时,如果拆分树后,树元素个数<=6,则会退化链表
退化情况2: remove树节点之前,若 root、root.left、root.right、root.left.left 有一个为 null,也会退化为链表

如果你都可以回答出来,那么恭喜你不要高兴得太早,这只是开始,回答完之后面试官会进行一连串的追问,如果都没有回答出来,也不要过于沮丧,相信看完这篇文章会收获很多!

如下所示,我们将元素a放入Hashmap中,需要经历计算hash值,再和capacity进行求模再计算出桶下标

在这里插入图片描述经过上述一系列的操作,相信不少的小伙伴会产生疑惑,到底有没有必要为了存储一个元素而通过这么复杂的步骤呢?

当然有必要,这样做是为了后续快速查找元素!

如下所示:

对于下述数组,我们将其放入Hashtable中,如果我们想查找元素a,那么需要比较10次才能够找到该元素

在这里插入图片描述

但如果将上述的数组的每个元素经过计算hash值,再和capacity进行求模再计算出桶下标,再放入HashMap中,如下所示:

此时我们如果想要查找元素a,那么可以直接通过计算桶下标进而确定元素a在下标为1的位置,这样我们只比较了一次。

在这里插入图片描述

但上述这种是理想状态下,我们所查找的元素所处的下标只包含一个元素,此时的时间复杂度为O(1)

既然有最优情况,也就会有最极端的情况,如果当非常多的元素的桶下标计算出来都是相同的呢?

实例:

在这里插入图片描述

当我们想要查找元素8时,对于上述这种情况,即使计算出桶下标,但我们似乎还是需要比较8次,对此我们有两种解决思路-----------1:扩容,2:使用红黑树

使用扩容:

当元素的含量超过容器的3/4,就会进行扩容操作,如下所示:

在这里插入图片描述

元素5,6,7,8的桶下标发生改变的原因为:此时的容量发生变化导致取模的结果也发方生变化,扩容后链表长度缩减

但扩容一定会导致链表长度的缩减吗?当然不是,当扩容后,没有元素桶下标的变化,自然就不会发生链表长度的缩减,如下所示:

在这里插入图片描述

对此,我们只有进行红黑树操作了

红黑树化:

当链表长度超过8并且总容量大于64,才会产生红黑树,而如果仅仅满足链表长度超过8时,会先通过扩容从而缩减链表长度,当扩容到64以后,才会红黑树化

举例:

在这里插入图片描述

当我强制性的将a添加到桶下标为1的位置时,此时即使链表长度超过了8,但也不会生成红黑树,而是优先进行扩容操作,如下所示:

在这里插入图片描述

继续向桶下标为33的地方添加元素,此时总容量大于64已经满足,且添加过后,该链表长度也超过8,因此会发生红黑树化

在这里插入图片描述

红黑树的特点:无论是根节点还是任意的子根节点都满足,(子)根节点左边的元素均小于(子)根节点,(子)根节点右边的元素均大于右(子)根节点

红黑树依然可以提高我们的查询效率,比如,此时我们需要查找元素8,那么只需要先确定元素8的桶下标,然后和4比较,再和6比较,最后和8比较,经过三次比较就可以找到元素8

链表的搜索时间复杂度为O(n),而红黑树的时间复杂度为O(log2^n)

注:某个桶下标的链表长度是可以超过8的,当总容量小于64时,并不会发生扩容,即使每次添加的桶下标为一个值,都不会红黑树化

索引的计算方法:

1:计算索引的hashCode(),再调用HashMap的hash()方法进行二次哈希,最后&(capacity-1)得到索引

2:二次hash()是为了综合高位数据,让哈希分布更为均匀

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

但1,2,3,都是为了配合容量为2的n次幂时的优化手段,例如:Hashtable的容量就不是2的n次幂,并不能说哪种设计更优,应该是设计者综合了各种因素,最终选择了使用2的n次幂作为容量

HashMap_put:

put方法流程,1.7和1.8有何不同?

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

2:计算索引(桶下标)

3:如果桶下标还没人占用,创建Node占位返回

4:如果桶下标已经有人占用

1:已经是TreeNode走红黑树的添加或更新逻辑
2:是普通Node,走链表的添加或更新逻辑,如果链表长度超过树化阈值,走树化逻辑

5:返回前检查容量是否超过阈值,一旦超过则进行扩容

1.7 and1.8 的不同点:

链表插入节点时,1.7是头插法,1.8是尾插法
1.7是大于等于阈值且没有空位时才扩容,而1.8是大于阈值就扩容
1.8在扩容计算Node索引时,会优化---将哈希值与原来的哈希值进行按位与再判断

加载因子为何默认为0.75f?

在空间占用与查询时间之间取得较好的权衡
大于这个值,空间节省了,但链表就会比较长影响性能
小于这个值,冲突减少了,但扩容就会更频繁,空间占用多

多线程下会引发什么问题?

1.7:扩容死链
1.7和1.8数据错乱

扩容死锁的引发过程:

单线程下扩容的过程:

准备工作:

在这里插入图片描述

第一轮循环:

在这里插入图片描述

第二轮循环:

在这里插入图片描述

第二次循环结束,e指向下一个元素,进行第三次循环:

在这里插入图片描述

对象实现迁移并没有使得对象发生变化,只是对象的引用地址发生了变化,由于是头插法,因此使得迁移后的对象顺序发生了颠倒

多线程下扩容的过程:

在这里插入图片描述
在这里插入图片描述

第一轮循环:

在这里插入图片描述

第一轮循环结束:

在这里插入图片描述

第二轮循环:

在这里插入图片描述

在这里插入图片描述

第二轮循环结束:

在这里插入图片描述

第三轮循环开始:

在这里插入图片描述

在这里插入图片描述

第三轮循环结束:

在这里插入图片描述

key能否为null,作为key的对象有什么要求?

HashMap的key可以作为null,但Map的其他实现则不然,比如treemap,作为key的对象,必须实现hashCode和equals,并且key的内容不能修改

不能修改的原因:

HashMap用Key的哈希值来存储和查找键值对,当插入一个Entry时,HashMap会计算Entry Key的哈希值,Map会根据这个哈希值把Entry插入到相应的位置,查找时,HashMap通过计算Key的哈希值到特定位置查找这个Entry,如果HashMap Key的哈希值在存储键值对后发生改变,Map可能再也查找不到这个Entry了,如果Key对象是可变的,那么Key的哈希值就可能改变,在HashMap中可变对象作为Key,会造成数据丢失,如果可变对象在HashMap中被用作键,那就要小心在改变对象状态的时候,不要改变它的哈希值了

必须实现hashCode和equals的原因:

如果只重写hashcode()不重写equals()方法,当比较equals()时,其实调用的是Object中的方法,只是看他们是否为同一对象(即进行内存地址的比较)

如果只重写equals()不重写hashcode()方法,在判断的时候,会被拦下HashMap认为是不同的Key,以对象作为HashMap的key,必须重写该对象的hashCode和equals方法,确保hashCode相等的时候equals的值也是true

String对象的hashCode()如何设计的,为什么每次乘的是31?

目的是达到较为均匀的散列效果,每个字符串的hashCode足够独特

字符串中的每个字符都可以表现为一个数字,称为Si,其中i的范围是0-n-1散列公式为:S0*31^ n-1+S1*31^ n-2......Si*31^ n-1-i+..........Sn-1*31^031带入公式有较好的散列特性,并且31*h可以被优化为:
1:即32*h-h
2:即2^5*h-h
3:即h<<5-h

举例:

公式套入31的散列效果如下:

在这里插入图片描述
蓝色线条为公式套入2的散列效果如下:

在这里插入图片描述

蓝色线条为公式套入11的散列效果如下:

在这里插入图片描述
蓝色线条为公式套入41的散列效果如下:

在这里插入图片描述

通过上述几组数据的对比,我们不难看出31和41套入公式的散列性是比较好的,那么为什么选择31而不是41呢?

原因是:我们不仅得保证有一个较好的散列性,而且还需要保证经过加减法可以优化为2的n次幂,31就满足,但41并不能满足

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

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

相关文章

【Redis中bigkey你了解吗?bigkey的危害?】

一.Redis中bigkey你了解吗&#xff1f;bigkey的危害&#xff1f; 如果面试官问到了这个问题&#xff0c;不必惊慌&#xff0c;接下来我们从什么是bigkey&#xff1f;bigkey划分的类型&#xff1f;bigkey危害之处&#xff1f; 二.什么是bigkey&#xff1f;会有什么影响&#xff…

苹果设计可变色Apple Watch表带,智能穿戴玩法多

苹果最新技术专利显示&#xff0c;苹果正在为 Apple Watch 设计一款可变色的表带&#xff0c;可以根据佩戴者所穿着的服装、所在的环境等自动改变颜色。据介绍&#xff0c;这款表带里的灯丝具有电致变色功能&#xff0c;可以通过施加不同的电压&#xff0c;来实现显示多种颜色或…

jvm常识

Jvm工作原理学习笔记0126一、JVM的生命周期1.JVM实例对应了一个独立运行的java程序它是进程级别a)启动。启动一个Java程序时&#xff0c;一个JVM实例就产生了&#xff0c;任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点b)运行。ma…

web中git漏洞的形成的原理及使用

目录 1.Git漏洞的成因 1.不正确的权限设置&#xff1a; 2.代码注入漏洞&#xff1a; 3.未经身份验证的访问&#xff1a; 4.非安全传输&#xff1a; 5.跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a; 2.git泄露环境的搭建 git init&#xff1a; git add&#xff1…

跟小米、特斯拉分“蛋糕”的优必选要IPO

‍数据智能产业创新服务媒体——聚焦数智 改变商业如果要问目前科技界最火的话题是什么&#xff0c;很多人的答案将是ChatGPT。而且&#xff0c;ChatGPT大有“破圈”之势&#xff0c;不仅业界人士在关注&#xff0c;各行各业的普通人也在大量讨论。要说最近科技圈讨论的焦点&a…

C++【模板STL简介】

文章目录C模板&&STL初阶一、泛型编程二、函数模板2.1.函数模板概念2.2.函数模板格式2.3.函数模板的实例化2.4.模板参数的匹配原则三、 类模板3.1.模板的定义格式3.2.类模板的实例化STL简介一、STL的概念、组成及缺陷二、STL的版本C模板&&STL初阶 一、泛型编程…

Allegro如何显示层叠Options和Find操作界面

Allegro如何显示层叠Options和Find操作界面 Allegro常规有三大操作界面,层叠,Options和Find,如下图 软件第一次启动的时候,三大界面是关闭的,下面介绍如何把它们打开,具体操作步骤如下 点击菜单上的View点击Windows

秒懂算法 | 回归算法中的贝叶斯

在本文中,我们会用概率的观点来看待机器学习模型,用简单的例子帮助大家理解判别式模型和生成式模型的区别。通过思考曲线拟合的问题,发现习以为常的损失函数和正则化项背后有着深刻的意义 01、快速理解判别式模型和生成式模型 从概率的角度来理解数据有着两个不同的角度,假…

MySQL调优

MySQL调优 数据库优化常见方案 优化shema,sql语句索引加缓存&#xff0c;memcached,redis主从复制&#xff0c;读写分离垂直拆分水平拆分 为了知道怎么优化SQL,必须先清楚SQL的生命周期 SQL生命周期 应用服务器连接数据库服务器&#xff0c;建立一个TCP/IP连接&#xff0c…

公网远程连接Oracle数据库【内网穿透】

文章目录1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程OracleOracle&#xff0c;是甲骨文公司的一款关系数据库管理系…

【OpenAI】基于 Gym-CarRacing 的自动驾驶练习项目 | 路径训练功能的实现 | GYM-Box2D CarRacing

限时开放&#xff0c;猛戳订阅&#xff01; &#x1f449; 《一起玩蛇》&#x1f40d; &#x1f4ad; 写在前面&#xff1a; 本篇是关于多伦多大学自动驾驶专业项目的博客。GYM-Box2D CarRacing 是一种在 OpenAI Gym 平台上开发和比较强化学习算法的模拟环境。它是流行的 Box2…

数据库浅谈之 Bloom Filter

数据库浅谈之 Bloom Filter HELLO&#xff0c;各位博友好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 这里是数据库浅谈系列&#xff0c;收录在专栏 DATABASE 中 &#x1f61c;&#x1f61c;&#x1f61c; 本系列阿呆将记录一些数据库领域相关的知识 &am…

亚马逊短期疲软,但长期前景乐观

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 由于投资者对亚马逊(AMZN)前景的担忧&#xff0c;导致该公司的股价在过去一年中下跌了39%。然而猛兽财经认为亚马逊近期面临的不利因素只是暂时的&#xff0c;该公司还是有充分的条件可以在医疗保健和物流领域获得重大增长机…

华为OD机试题,用 Java 解【N 进制减法】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

华为OD机试题,用 Java 解【快递运输】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

我希望早点知道的关于成长的建议

人上了年纪&#xff0c;往往在诸如更加闭塞&#xff0c;更加固执这些缺点之外&#xff0c;再多出来一个缺点&#xff1a;那就是动不动就爱给别人建议。我当然也未能免俗。有时候会听到同样悲观且固执的过来人告诉我&#xff0c;这些建议说了和没说效果都一样&#xff0c;人们在…

「媒体邀约」四川有哪些媒体,成都活动媒体邀约

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 四川省位于中国西南地区&#xff0c;是中国的一个省份。成都市是四川省的省会&#xff0c;成都市是中国西部地区的政治、经济、文化和交通中心&#xff0c;也是著名的旅游胜地。每年的文…

关于iframe一些通讯的记录(可适用工作流审批,文中有项目实践,欢迎咨询)

一.知识点(1).我们可以通过postMessage(发送方)和onmessage(接收方)这两个HTML5的方法, 来解决跨页面通信问题&#xff0c;或者通过iframe嵌套的不同页面之间的通信a.父页面代码如下<div v-if"src" class"iframe"><iframeref"iframe"id…

Linux——进程概念(进程状态)

目录 进程状态 三态模型 五态模型 七态模型 Example eg1:阻塞态&#xff1a;等待某种资源的过程 eg2:挂起态 Linux内核源代码 Linux进程状态查看 Linux运行状态 R运行状态&#xff08;running&#xff09;: S睡眠状态&#xff08;sleeping)&#xff1a; D磁盘休眠状…

HEVC 编码速率控制

视频传输带宽通常都会受到一定的限制&#xff0c;为了在满足通信带宽和传输时延限制的情况下有效传输视频数据&#xff0c;保证视频业务的播放质量&#xff0c;需要对视频编码过程进行速率控制&#xff0c;所谓速率控制&#xff0c;就是通过选择一系列编码失真尽量小&#xff0…