HashMap 1.8有什么变化?改进在哪里

HashMap

数据结构

  • jdk1.7 使用数组+链表的结构, 链表采用的是头插法(多线程下会有形成循环链表的危险)

  • jdk1.8 使用数组+链表+红黑树的结构, 链表和红黑树是使用的尾插法

1.7 与 1.8的差别以及为什么这样设计?

  • 在数据结构上 1.7时使用的是 数组+链表 这种数据结构,1.8时使用的是 数组+链表+红黑树 的数据结构,减少hash冲突情况下的寻址时间

  • 在hash定位的时候, 1.7直接使用key的hash值 & n-1进行与或位运算,1.8 则先将hash进行了一次扰动处理,key的hash ^ hash>>>16位后再与 n-1 进行与运算,使hash分布更为均匀减少hash冲突

  • 再扩容动作上 链表上的数据插入方式 从1.7头插法改为1.8的尾插法,解决了在并发情况下resize导致的链表循环指针问题

数据结构为什么改为 数组+链表+红黑树

当每个mapentry中链表长度大于8时(且容量大于64), 会由链表转换为红黑树(减少hash冲突情况下的寻址时间复杂度 O(n)降低为O(logn)

为什么选择8这个临界值?

根据泊松分布,链表长度大于8的情况非常小,极大多数的情况都可以被链表的情况覆盖

在长度小于8的时候,链表和红黑树的性能相差不多,但是转化为树还需要时间和空间

当mapentery中的红黑树长度降低小于6的时候,会由红黑树转回为链表结构(6和8起到了很好的缓冲作用, 避免了频繁的数据结构转换

为什么选择红黑树而不是二叉树呢?

普通二叉树会出现节点分布及其不平衡的情况

AVL和平衡二叉树之间差别

AVL树更加平衡,可以提供更快的查询能力,适合查找密集形的任务

红黑树适合插入密集型任务,在插入的时候使用较少的旋转

为什么对hash值进行一次干扰呢

对n-1做与运算的时候,n通常比较小,n-1的二进制,大概率高位补0,这样做与运算的时候key对应hash值的前16相当于从来都不参与计算,

通过右移再进行异或的操作,使后16位具有了前16位的特征,使hash分布更为均匀

为什么使用尾插法

hashmap分为两步

- 扩容:创建一个新的Entry空数组,长度是原数组的2倍。

- ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。

在头插法的情况下并发扩容存在链表成环的这种情况,改为尾插法避免了这种情况

posted on 2021-05-24 17:02  闻道又又鱼  阅读(328)  评论(0)    收藏  举报

导航