HashMap讲解(包括产生死循环问题的原因)

HashMap是由由数组和链表组合构成的数据结构。

数组里面每个地方都存了Key-Value这样的实例,如下所示:

HashMap本身所有的位置都为null,在put插入的时候会根据keyhash值去计算一个index值,index值即表示在HashMap中存放的位置。

如:将为了将键值对("Bull", 1)存入HashMap中,所计算得到的index值为1:

同时,如果再次将一个键值对("Red", 2)存入当前HashMap中,若计算得到的index值仍为1,则回以如下形式存入HashMap中:

 上述情况即为以链表的形式进行存储。

每一个节点都会保存自身的hash、key、value、以及下个节点。源码如下。

同时,需要注意的是,新的Entry节点在插入链表的时候,插入方法在不同的java版本有不同java8之前是头插法,就是说新来的值会取代原有的值,原有的值就顺推到链表中去,就像上面的例子一样。

但是,在java8之后,都是所用尾部插入了(使用头插法在多线程的情况下会产生循环链表的问题)。

产生死循环的原因如下:

首先,看一下HashMap的扩容机制:

HashMap扩容主要设计两个因素:

  • Capacity:HashMap当前长度。
  • LoadFactor:负载因子,默认值0.75f。

 HashMap在容量达到设定的阈值(0.75f)就会进行扩容。比如当前的容量大小为100,当你存进第76个的时候,判断发现需要进行resize了,那就需要进行扩容。

扩容具体步骤如下:

  • 扩容:创建一个新的Entry空数组,长度是原数组的2倍
  • ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。

这里进行ReHash的原因是,当长度扩大以后,Hash的规则也随之改变,即ReHash得到的index值因为数组长度的不同变得不同。

单线程扩容前:

  

 单线程扩容后(index不同):

单线程扩容后(极端情况下所得到的index值相同):

 在多线程情况下进行扩容:

现在假设线程T1与T2均指向第一个(“Bull“, 1)键值对,T1.next与T2.next均指向第二个("Red",2)键值对

现在两个线程两个线程均开始扩容,且此时线程T2的时间片恰好用完,则线程T1进行扩容,结果为:

 

 此时,线程T2所指向的键值对均没有改变(对线程T1的ReHash操作不知情),则此时对于线程T2就达成了一个死循环:

 如果线程T2此时去取值,则出现无限循环。

HashMap的resize()方法是用于扩容HashMap的方法。当HashMap中存储的数据量大于threshold时或进行初始化HashMap时,会触发resize()方法进行扩容操作。\[1\] 在HashMap的putVal()方法中,会先判断table是否为空,如果为空,则会执行resize()方法进行初始化table。\[1\] 在HashMap中,当存储的数据量大于threshold时,也会执行resize()方法进行扩容操作。\[1\] 在JDK1.8之前,扩容操作在多线程情况下容易造成环形链表,可能导致get操作产生死循环。而在JDK1.8中,resize()方法不再调用transfer()方法,而是直接将原来transfer()方法中的代码写在自己的方法体内。此外,扩容后新数组中的链表顺序与旧数组中的链表顺序保持一致,不再改变顺序。\[2\] #### 引用[.reference_title] - *1* [最详细HashMap集合源码讲解(resize()方法)](https://blog.youkuaiyun.com/weixin_37541878/article/details/119391236)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [HashMap的resize方法](https://blog.youkuaiyun.com/qq_38304320/article/details/103496039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值