前言
HashMap在实际开发过程中是使用频率最高的用于映射(键值对)处理的数据类型。在JDK1.8中对HashMap底层的底层实现进行了大幅度地优化,比如引入红黑树的数据结构和扩容的优化等等。
本文主要分析一下为什么 Map 桶中超过 8 个才转为红黑树? 面试中这个问题也是经常会提起的,接下来我们一起学习吧~
什么是“拉链法”?
在JDK 1.8 的 HashMap
和 ConcurrentHashMap
都有这样一个同样的特点,那就是最开始的Map
是空的,因为里面没有任何元素,往里放元素时会计算 hash
值,计算之后,第 1 个 value
会首先占用一个桶(也被称为槽点)位置,后续如果经过计算发现需要落到同一个桶中,那么会使用链表的形式往后延长,俗称“拉链法”,如图所示:
图中,有的桶是空的, 比如第 4 个;有的只有一个元素,比如 1、3、6;有的就是刚才说的拉链法,比如第 2 和第 5 个桶。
当链表长度大于或等于阈值(默认为 8)的时候,如果同时还满足容量大于或等于 MIN_TREEIFY_CAPACITY
(默认为 64)的要求,就会把链表转换为红黑树。同样,后续如果由于删除或者其他原因调整了大小,当红黑树的节点小于或等于 6 个以后,又会恢复为链表形态。
我们来看看HashMap
的结构示意图,如下图所示:
在图中可以看到,有一些槽点是空的、一些是拉链、一些是红黑树。
在面试中经常会被问到,为什么会链表会为红黑树以及红黑树有哪些特点,可是,为什么转化的这个阈值要默认设置为 8 呢?
要想知道为什么设置为 8,那首先我们就要知道为什么要转换,因为转换是第一步。
每次遍历一个链表,平均查找的时间复杂度是 O(n)
,n 是链表的长度。红黑树有和链表不一样的查找性能,因为红黑树有自平衡的特点,可以防止不平衡情况的发生,所以说可以始终将查找的时间复杂度控制在 O(log(n))
。
最初链表还不是很长,所以可能 O(n)
和 O(log(n))
的区别也不大,但是如果链表变得越来越长,那么区别就会变的很明显,所以为了提升查找性能,需要把链表转化为红黑树的形式。