HashMap 链表插入方式 → 头插为何改成尾插 ?

数据结构
  对 HashMap 的底层数据结构,相信大家都有所了解,不同的版本,底层数据结构会有所不同

1.7 的底层数据结构

View Code
  1.8 的底层数据结构

View Code
  但基础结构还是: 数组 + 链表 ,称作 哈希表 或 散列表

只是 1.8 做了优化,引进了 红黑树 ,来提升链表中元素获取的速度

JDK1.7 头插
  只有元素添加的时候,才会出现链表元素的插入,那么我们先来看看 put 方法

put - 添加元素
    源码如下

View Code
    直接看代码可能不够直观,我们结合流程图来看

什么? 还是不够直观? (楼主也这么觉得)

那我们就结合具体案例来看下这个流程

假设 HashMap 初始状态

然后依次往里面添加元素:(2,b), (3,w), (5,e), (9,t), (16,p)

再利用断点调试,我们来看看真实情况

一切都对得上,进展的也挺顺利

resize - 数组扩容
    上述提到了扩容,但是没细讲,我们来看看扩容的实现

关键代码如下

View Code
    主要做了两件事:1、创建一个新的 Entry 空数组,长度是原数组的 2 倍,2、遍历原数组,对每个元素重新计算新数组的索引值,然后放入到新数组的对应位置

有意思的是这个转移方法:transfer,我们结合案例来仔细看看

假设扩容之前的状态如下图所示

扩容过程如下

利用断点调试,我们来看看真实情况

链表元素的转移,还是采用的头插法

链表成环
    不管是元素的添加,还是数组扩容,只要涉及到 hash 冲突,就会采用头插法将元素添加到链表中

上面讲了那么多,看似风平浪静,实则暗流涌动;单线程下,确实不会有什么问题,那多线程下呢 ? 我们接着往下看

将设扩容之前的的状态如下所示

然后,线程 1 添加 (1,a) ,线程 2 添加 (19,n),线程 1 会进行扩容,线程 2 也进行扩容,那么 transfer 的时候就可能出现如下情况

哦豁,链表成环了,这就会导致:Infinite Loop

JDK1.8 尾插
  1.8就不讲那么详细了,我们主要来看看 resize 中的元素转移部分

View Code
  通过尾插法,维护了链表元素的原有顺序

在扩容时,头插法会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题,而尾插法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值