hashmap8如何解决hashmap7中的环链问题

先说说对hashmap8的理解:
当我们new一个hashMap的时候,其实没有做什么多余操作,几乎所有的操作都是在put方法里面进行的,在map.put()方法中,底层是调用了this.putVal()方法,此方法是核心方法。里面做了如下逻辑:
1、判断hashmap是否需要扩容,如果需要则进行扩容,核心方法是resize()。
这个方法非常重要,也非常的消耗性能,因为涉及到对其中的元素进行地址再分配。既然要重新分配地址,那自然也免不了对每个hash桶的元素挨个遍历,重新计算地址,这就是为什么底层源码会十分注重性能,以前有的同学一直对hashmap在计算hash散列地址的方法抱有怀疑,原本计算hash值是一件非常简单的事情-——hashvalue/length就行了,为什么一定要使用位与运算,hashval&(length-1)?这样不光难以理解,而且要保证length是2的指数次幂,岂不是多此一举?
这是因为在计算机中取模运算的性能远远低于位与运算,而hashmap这种数据结构又经常需要扩容,那么数据量一旦大起来,需要逐个遍历其中元素,计算hash散列地址,那么这个hash散列地址的算法就会大大影响性能了。
再提一下,resize()这个方法,hashmap7中著名的环链问题就是由它引起的,这是面试中常见的问题,大家有兴趣可以去研究下。

2、判断hash散列的桶中是否有值,如果有值,就生成链表
我们都知道,hashmap的早期数据结构(1.7以前),主要是数组和链表组成的,链表就是所谓的hash桶,如果元素在表内的散列地址值发生重叠时,就会生成链表

3、判断hash桶内元素是否大于默认阈值TREEIFY_THRESHOLD - 1,如果是,则生成红黑树
在这里插入图片描述
如上所说,1.7以前我们hashmap主要数据结构是数组和链表,那么1.8相比于1.7发生最大的改变就是增加了红黑树,也就是说,1.8的的hashmap是由数组,链表,红黑树三种数据结构组成的,当链表的值达到一定数量的时候,链表就会转化成红黑树,这个默认的数量就是TREEIFY_THRESHOLD - 1,然后可能有的同学会问了,为什么要把链表转化为红黑树呢?
这个问题其实一两句说不清楚,可以总结为,一切都是为了查询效率。大家知道,链表是一个查询较慢的数据结构,查询复杂度达到了O(n),是一个线性数据结构,而红黑树查询复杂度只有O(logn),红黑树的树高不会膨胀的那么厉害,效率较高。
然后,可能又有同学要问了,为啥阈值是TREEIFY_THRESHOLD - 1?这个问题我先不说,卖个关子,留给大家去研究。

4、回到主题,hashmap7和hashmap8之间有啥区别呢?hashmap8是如何解决hashmap7中的环链问题的呢。首先,我们清楚环链问题是怎么造成的。
在hashmap1.7,扩容的resize()方法中,需要对链表中的元素重新计算hash值,迁移到新的tab中去,然而因为效率问题,源码开发者认为新插入的元素访问热度较高,所以采用了头插法,也就是说链表尾部的元素将会被提到前面来,是个反顺序,链表中的元素顺序被倒置了。这样一来,在多线程中就有形成闭环的可能。
例如:当线程A执行到链表元素a时,得出它的next元素是b,刚好时间片切换到线程B,线程B将a,b两个元素都倒置存入到新的tab中,注意,现在b的next就是a了!正当此时,线程A醒来,在线程A中,a的next是b。。。。
也就是说,对于线程a来说,a的next是b,而b的next是a,完美,环链形成!!!
如上,我们从例子中得知了,hashmap7形成环链的大概过程,为了避免环链问题的形成,hashmap8是怎么做的呢?
紧接着,我们需要分析出环链形成的根本原因,那就是因为采用了头插法。头插法首尾颠倒,反转了链表中元素的顺序,才使得线程间形成环链。那么解决这个问题的方式最好的办法,自然就是不使用头插法了,事实上,hashmap8也是怎么做的。
在hashmap8中,链表数据的迁移采用的是尾插法,不会再颠倒链表(或者说红黑树)元素的顺序了,当然,除此之外,为了最大限度的减少树高和hash碰撞的概率,hashmap8还引出了高低位指针的概念,如下图代码:
在这里插入图片描述
一条链表要做数据迁移到新的tab下,那么这个散列地址的算法跟hashmap7已经有了区别,它首先需要经过e.hash & oldCap这行代码来决定链表元素的高低位置,等于是分为了两条链表,低位元素仍然在老的位置,而高位元素却到了当前位置+oldCap的位置,一条链表被拆成了两条。这样做的好处如下:
①省却了再次计算hash值的开销,避免每个key都进行rehash
②采用尾插法,避免了环链的形成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章