hashMap相关

news2024/11/16 8:47:09

文章目录

    • HashMap
      • HashMap介绍
      • HashMap在 JDK1.7和 JDK1.8中的区别
      • JDK1.7中HashMap头插法死循环的原因
      • HashMap的底层原理
      • HashMap的扩容机制
      • 解决Hash冲突的方法
      • 为什么在解决hash冲突的时候选择先用链表,再转红黑树?
      • HashMap为什么线程不安全
      • 一般用什么作为HashMap的key?

————————————————————————————————————

HashMap

HashMap介绍

特点

存储形式key value

无序不可重复,key值保障不会重复

初始化容量16,官网推荐为2的倍数,为了散列均匀,提交存取效率,默认加载因子0.75,到75%的时候会扩容,扩容:扩容之后是原容里2倍。0.75是对时间和空间上的一个平衡选择。

key 存储的时候会调用底层hashcode(),hashcode是一串数字,然后会进行取余操作

时间复杂度 每一次取数据O(1) , 大多数每一次插入数据O(1) ,理论上增删改查都是O(1)

HashMap在 JDK1.7和 JDK1.8中的区别

  • 组成结构

    • 在 JDK1.7 中,由"数组+链表"组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。

      在JDK1.8中,则是由"数组+链表+红黑树"组成,当链表长度大于 8 并且 数组的长度大于 64 的时候,再向链表中添加元素链表就会转化成红黑树,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间。

  • JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,头插法就是能够提高插入的效率,但是在并发情况下也会容易出现循环链表问题。尾插法,能够避免出现循环链表的问题。

    • 为什么从头插法改成尾插法?
      • 在1.7中,是没有红黑树的并发的情况下单链表过长会成环发生死循环
      • 在1.8中,尾插法就可以解决这个问题
  • 1.7中hash值是可变的1.8中hash是final修饰,不可变,因为有rehash的操作。1.8中hash是final修饰,也就是说hash值一旦确定,就不会再重新计算hash值了。而且为了转换为红黑树新增了一个TreeNode节点。

  • JDK1.7中,HashMap存储的是Entry对象

    JDK1.8当中,HashMap存储的是实现Entry接口Node对象

  • 扩容机制

    • 1.7中是先扩容后插入,1.8中是先插入再扩容
    • JDK1.7的时候是先扩容后插入的,扩容过程中会将原来的数据,放入到新的数组中,但是会重新计算hash值进行分配,这样就会导致无论这一次插入是不是发生hash冲突都需要进行扩容,如果这次插入的并没有发生Hash冲突的话,那么就会造成一次无效扩容,但是在1.8的时候是先插入再扩容的,优点是可以减少1.7的一次无效的扩容,因为如果这次插入没有发生Hash冲突的话,那么其实就不会造成扩容

JDK1.7中HashMap头插法死循环的原因

在jdk1.7中HashMap底层使用的是数组加链表的形式,并且在数据插入的时候采用的是头插法,也就是说新插入的数据会从链表的头结点进行插入。比如说扩容之前是有ABC三个节点依次挂在一条链表上。

第一步:这时候有两个线程T1和T2都准备对HashMap进行扩容操作,此时T1和T2指向的都是链表的头结点A,而T1和T2的下一个节点分别是T1.next和T2.next,他们都指向B,

image-20221030090047701

第二步:开始扩容,这个时候,假设T2的时间片正好用完,进入了休眠状态,而线程T1开始执行扩容操作,一直到T1扩容完成后,T2才被唤醒,这个时候T2.next的指向依然没有变。

image-20221030090329966

因为HashMap采用的是头插法线程1执行后,链表中的节点顺序发生了变化,但是线程2对这一切还是不知道的,它的执行还是不变的,所以此时T1执行完成,T2开始执行,死循环就发生了。因为T1扩容完成后,B节点的下一个节点是A,而T2线程指向的首节点是A,第二个节点是B,这个顺序刚好和T1扩容之后的节点顺序是相反的。T1执行完之后是B到A,这样A和B两个节点就发生了死循环。

HashMap的底层原理

  • JDK1.8之前,HashMap由数组+链表组成,数组是主体,链表为了解决冲突问题,HashMap通过key的hashcode经过扰动函数处理得到hash值,然后经过计算(n-1)&hash判断存储位置,当前如果和当前位置的值相同则覆盖,不同则拉链法解决冲突。扰动函数可以减少碰撞。

  • JDK1.8之后,HashMap加了红黑树,当链表长度大于阈值(默认为8),数组长度大于64的时候,会执行链表转红黑树,来加快搜索速度。只有当数组长度大于等于64的时候,才会执行转换红黑树以减少搜索时间,否则通过resize()方法对数组进行扩容。

HashMap的扩容机制

我们创建一个HashMap的集合对象,底层node数组默认大小是16,当集合的存储容量达到一个临界值的时候会扩容,loadFactor * capacity (负载因子 * 容量),负载因子默认是0.75。

  • 在jdk1.8中 ,一条链表的元素个数已经到8个,并且table数组长度达到64了,链表就会树化成红黑树
  • 如果仅仅是链表元素个数到8 , 数组长度还没到64 , 那么就不会树化,而是数组长度会扩容为2倍 , 然后重新哈希,当然链表的长度可能超过8
  • 但是如果红黑树的元素个数小于6 那么就会还原成链表, 当红黑树的元素个数不小于32的时候才会再次扩容

解决Hash冲突的方法

解决Hash冲突方法有:开放定址法、再哈希法、链地址法(拉链法)、建立公共溢出区。HashMap中采用的是拉链法

​ ①:开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从 hash 表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。(基本思想就是,如果p=H(key)出现冲突时,则以p为基础,再次hash,I p1=H§ ,如果p1再次出现冲突,则以p1为基础,以此类推,直到找到一个不冲突的哈希地址pi。因此开放定址法所需要的hash表的长度要大于等于所需要存放的元素,而且因为存在再次hash,所以只能在删除的节点上做标记,而不能真正删除节点。)ThreadLocal 就用到了线性探测法来解决 hash 冲突的。

​ 比如像这样一种情况,在 hash 表索引 1 的位置存了一个 key=name,当再次添加key=hobby 时,hash 计算得到的索引也是 1,这个就是 hash 冲突。而开放定址法,就是按顺序向前找到一个空闲的位置来存储冲突的 key。

​ ②:链式寻址法,这是一种非常常见的方法,简单理解就是把存在 hash 冲突的 key,以单向链表的方式来存储,比如 HashMap 就是采用链式寻址法来实现的。

​ ③:再 hash 法,就是当通过某个 hash 函数计算的 key 存在冲突时,再用另外一个hash 函数对这个 key 做 hash,一直运算直到不再产生冲突。这种方式不易产生堆集,但是会增加计算时间,性能影响较大。

​ ④:建立公共溢出区,就是把 hash 表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放入到溢出表中。

为什么在解决hash冲突的时候选择先用链表,再转红黑树?

因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。当元素小于8个的时候,此时做查询操作,链表结构已经能保证查询性能。当元素大于8个的时候,红黑树搜索时间复杂度是O(logn),而链表是O(n),此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。

因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。

HashMap为什么线程不安全

  • 多线程下扩容可能造成死循环。JDK1.7中的HashMap使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的岀现,形成死循环。所以在 JDK1.8 改成了尾插法插入元素,在扩容时会保持链表元素原本的顺序,不会岀现环形链表的问题。

  • 多线程的put可能导致元素的丢失。多线程同时执行put操作,如果计算岀来的索引位置是相同的,那会造成前一个key被后一个key覆盖,从而导致元素的丢失。此问题在JDK 1.7和JDK 1.8中 都存在。

  • put和get并发时,可能导致get为null。线程1执行put时,因为元素个数超出threshold(临界点进行扩容)而导致 rehash(重新计算哈希值),线程2此时执行get,有可能导致这个问题。此问题在JDK 1.7和JDK 1.8中都存在。

一般用什么作为HashMap的key?

一般用Integer、 String 这种不可变类当HashMap当key,而且String最为常用。
● 因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就是HashMap中的键往往都使用字符串的原因。
● 因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的,这些类已经很规范的重写了hashCode(以及equals()方法。

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

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

相关文章

程序员需要达到什么水平才能顺利拿到 20k 无压力?

很有趣的是,在程序员身上,我看到了最明显,也最有趣的贫富差距。 根据2022最新版大厂新入职员工职级对应表,大厂技术线的员工轻而易举地拿到了20w的水平,而只要往上够一够,30w也不是什么难事。 然而&#xf…

玩转云服务器:怎样用云服务器架设大型3D魔幻手游【魔域】服务器,实现联机多人同玩,带你一起搞机,了解游戏搭建过程,详细教程

准备工作: 你首先要准备一台云服务器! 服务器配置:2核4G以上配置! 服务器系统:win2012 开始搭建: 下载游戏服务端(有些多人叫源码,这里我就不解释了,喜欢怎么叫就怎…

SpringCloud Alibaba | 网关(三) : SpringCloudGateway 过滤器获取application/json中body数据

SpringCloudGateway 过滤器获取application/json中body数据一、前言二、通过cachedRequestBodyObject缓存获取三、ServerHttpRequest getBody方法获取四、(* ̄︶ ̄)一、前言 项目接口需要加解密,就在网关层进行解密操作。那么问题来了怎么在gateway 的filt…

基于松鼠算法改进的DELM预测-附代码

松鼠算法改进的深度极限学习机DELM的回归预测 文章目录松鼠算法改进的深度极限学习机DELM的回归预测1.ELM原理2.深度极限学习机(DELM)原理3.松鼠算法4.松鼠算法改进DELM5.实验结果6.参考文献7.Matlab代码1.ELM原理 ELM基础原理请参考:https:…

线程池相关

文章目录为什么需要线程池?池化思想常用方法execute()方法submit()方法shutdownisShutdownisTerminatedawaitTerminationshutdownNow创建线程池 七个参数流程JAVA线程池有哪几种类型?线程池常用的阻塞队列有哪些?源码中线程池是怎么复用线程的?如何合理配置线程池…

EMQX Cloud 自定义函数实现多种 IoT 数据形式的灵活转化

物联网场景中,各类设备终端的种类繁杂,所使用的通信协议各异,从而使得应用层的数据格式也各不相同。为了帮助用户实现统一数据格式,EMQX Cloud 最近推出了自定义函数功能:根据用户自定义的脚本对设备上报的数据进行预处…

上美股份在港交所上市:预计全年利润下滑,一叶子收入持续走低

12月22日,上海上美化妆品股份有限公司(HK:02145,下称“上美股份”)在港交所上市。本次上市,上美股份的发行价格为25.20港元/股,为此前发行区间的最低值。据此计算,上美股份的募资总额约为9.31亿…

CDH6.3.2集成Apache Atlas2.1.0

1 环境准备 1.1 CDH6.3.2 环境搭建 参考文档如下 Cloudera Manager安装CDH6教程-(一)虚拟环境安装配置 Cloudera Manager安装CDH6教程-(二)搭建Cloudera和CDH6 CM和CDH在安装的时候遇到的问题 CDH6.3.2 各组件版本 1.2 apa…

火爆“有机新消费”驶入酱油赛道 好记打造我国有机酱油行业领导品牌

根据观研报告网发布的《2022年中国有机酱油市场分析报告-市场竞争策略与发展动向前瞻》显示,有机酱油是指采用有机农作物为原料酿制的酱油。有机酱油含有浓郁的酱香和脂香,是一种不可多得的上等调味品,适合于蘸食,红烧&#xff0c…

以技术创新践行社会责任,欧科云链斩获界面新闻年度双项大奖

12月20日,欧科云链凭借在区块链技术领域的创新,与腾讯、宁德时代和埃森哲等各领域领先企业一同荣获“2022好公司行业领先大奖”。 12月21日,欧科云链以区块链科技之力积极践行社会责任,绽放商业向善的力量,与茅台、蚂蚁…

华为云Stack智能进化,三大举措赋能政企深度用云

【中国,深圳,2022年12月22日】今天,以“政企深度用云,释放数字生产力”为主题的华为云Stack战略暨新品发布会在线上举办,华为云围绕“让技术不难用、让场景变简单、让经验可复制”提出三大关键举措赋能政企迈向深度用云…

无线鼠标怎么连接电脑?2个方法,轻松学会

现在越来越多的小伙伴都喜欢使用无线鼠标。相比于有线鼠标,无线鼠标方便快捷,不会受到USB线的束缚。可是很多小伙伴买回无线鼠标,第一次使用的时候,却发现自己不知道怎么使用。无线鼠标怎么连接电脑?今天小编给大家带来…

对于转行Pyhon程序员,这些是我们需要知道的事情..

不知道从什么时候开始,我们焦虑当下,担心未来,踌躇和迷茫甚至痛苦成为我们的“生活伴侣”。 我无法定义这是好是坏,但我知道这肯定很难受。于是乎总很多人想寻得一个答案,希望可以抹平心中的波澜。而往往事与愿违。 …

口罩后,那些被“优化”的程序员都去哪儿了?

程序员在35岁真的会被裁吗? 被裁之后去哪儿工作 ,怎么办啊? 在很多社交平台,经常能看到不少小伙伴问出这样的问题,既迷茫,又慌张。有人说自己25岁就已经被裁了,也有人说做程序员,3…

江苏移动MGV3000-YS(S)/YS(M)-S905L3卡刷和线刷固件包

江苏移动MGV3000-YS(S)/YS(M)-S905L3卡刷和线刷固件包 固件特点: 1、修改dns,三网通用; 2、开放原厂固件屏蔽的市场安装和u盘安装apk; 3、无开机广告,无系统更新,不在被强制升级&#xff1b…

数据结构与算法java实战篇--链表

目录 一.链结点 二.单链表 1.单链表的插入方法insertFirst() 2.单链表的删除方法deleteFirst() 3.链表显示displayList() 4.单链表代码: 三. 查找和删除指定链结点 四.双端链表 五.用链表实现的栈 六.用链表实现队列 七.有序链表 八.双向链表 1.遍历 2.插入…

“量子深度学习的春天是否已来?” | CNCC论坛分享

2022年12月8日举办的CNCC2022“量子深度学习的春天是否已来?”技术论坛,从学术、技术和产业的角度出发,深入探讨未来5到10年深度学习发展在算力方面的瓶颈问题、量子深度学习会给AI领域带来何种演变以及量子深度学习的技术落地是否依然久远等…

代码随想录Day58|739. 每日温度、496.下一个更大元素 I

文章目录739.每日温度496.下一个更大元素 I739.每日温度 文章讲解:代码随想录 (programmercarl.com) 题目链接:739. 每日温度 - 力扣(LeetCode) 题目: 请根据每日 气温 列表,重新生成一个列表。对应位置…

weidl x DeepRec:热门微博推荐框架性能提升实战

微博推荐团队:陈雨、韩楠、蔡小娟、高家华 1.项目背景 热门微博是新浪微博的重要功能之一,包含热门流、热点流、频道流、小视频后推荐、视频社区等场景。 标推荐首页 发现页推荐 沉浸视频题weidl机器学习框架为热门微博在线学习提供模型训练和推理服务…

【问题记录与解决】ModuleNotFoundError: No module named ‘pymongo‘ 并测试代码

目录 一、问题记录二、解决方法一(不一定好使)三、法二(亲测可以使用)四、安装 pymongo 的目的一、问题记录 报错内容是缺少对应的模块, ModuleNotFoundError: No module named pymongo… 二、解决方法一(不一定好使) 直接在PyCharm中下载 pymongo模块即可。 但是这…