我们知道,JDK1.8 之后,HashMap 的底层数据结构类型为数组+链表+红黑树。红黑树本质上是一种二叉查找树,为了保持平衡,它又在二叉查找树的基础上增加了一些规则:

1.每个树节点要么是红色,要么是黑色;

2.根节点永远是黑色的;

3.所有的叶子节点都是黑色的(图中的 null 节点即为叶子节点);

4.每个红色节点的两个子节点一定都是黑色;

5.从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;

Python的红黑树数据结构 hashmap红黑树原理_平衡二叉树

那为什么要选择使用红黑树而不是用二叉树或者平衡二叉树呢?

因为红黑树是一种平衡的二叉树,其插入、删除、查找的最坏时间复杂度都为 O(logn),避免了二叉树最坏情况下的O(n)时间复杂度。之所以不用平衡二叉树是因为平衡二叉树是比红黑树更严格的平衡树,为了保持保持平衡,需要旋转的次数更多,也就是说平衡二叉树保持平衡的效率更低,所以平衡二叉树插入和删除的效率比红黑树要低。

红黑树如何保持平衡

红黑树有两种方式保持平衡:旋转和染色

旋转:旋转分为两种,左旋和右旋

Python的红黑树数据结构 hashmap红黑树原理_HashMap_02

 

Python的红黑树数据结构 hashmap红黑树原理_链表_03

染⾊

 

Python的红黑树数据结构 hashmap红黑树原理_红黑树_04

为什么链表转红黑树的阈值为 8?

链表转红黑树发生在 table 数组的长度大于 64 且链表长度大于 8 时,那么为什么链表转红黑树的阈值会是 8 呢?在源码中有一大段注释说明:红黑树节点的大小大概是普通节点大小的两倍,所以转红黑树,牺牲了空间来换取时间,更多的是一种兜底的策略,保证极端情况下的查找效率。

至于阈值为什么要选 8 呢?和统计学有关。理想情况下,使用随机哈希码,链表里的节点符合泊松分布,出现节点个数的概率是递减的,节点个数为 8 的情况发生概率仅为 0.00000006,小于百万分之一。

至于红黑树转回链表的阈值为什么是 6,而不是 8?是因为如果这个阈值也设置成 8,假如发生碰撞,节点增减刚好在 8 附近,会发生链表和红黑树的不断转换,导致资源浪费。