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此时去取值,则出现无限循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值