HashMap源码之treeifyBin()方法
Posted heaven-elegy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap源码之treeifyBin()方法相关的知识,希望对你有一定的参考价值。
/**
* tree桶方法.
* 一般用作桶结构变更后,将桶中过长的链表转换为红黑树
*/
final void treeifyBin(Node<K,V>[] tab, int hash) {
// n - 代表参数tab长度
// index - tab中表示hash的下标
// hash - 待处理的链表节点hash
// e - 目标节点
int n, index; Node<K,V> e;
// 判断tab是否为空或tab长度MIN_TREEIFY_CAPACITY=64
// 也就是说,在桶中单个链表长度可能已经达到要求(如putVal中的binCount >= TREEIFY_THRESHOLD - 1),但是桶容量未达标时,也不会进行tree化
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
// 表是空的或表容量小于MIN_TREEIFY_CAPACITY
// 重置大小
resize();
// 可以tree化,检查链表节点是否存在
else if ((e = tab[index = (n - 1) & hash]) != null) {
// 链表节点存在
// 树节点头与尾
TreeNode<K,V> hd = null, tl = null;
// 已经有第一个目标,直接do while
do {
// 构造一个TreeNode.(这里没有额外逻辑,仅仅是使用当前的e创建了TreeNode)
// 注意,这里的Tree继承自LinkedHashMap.Entry,内部包含了before与after的双向链表.但是TreeNode又自行实现了双向链表prev与next,并没有使用前者的数据结构
TreeNode<K,V> p = replacementTreeNode(e, null);
// 判断尾部是否为空
if (tl == null)
// 初始化头部
hd = p;
else {
// 尾部不为空
// 设置上一个节点
// 设置尾部下一个节点
p.prev = tl;
tl.next = p;
}
// 交换尾部
tl = p;
} while ((e = e.next) != null);
// 赋值并判断头部节点是否为空
if ((tab[index] = hd) != null)
// 调用TreeNode的treeify组装红黑树
hd.treeify(tab);
}
}
/**
* 组装红黑树
*/
final void treeify(Node<K,V>[] tab) {
// 根节点(黑色节点)
TreeNode<K,V> root = null;
// 进行迭代.(当前this作用域位于TreeNode实例)
// x表示当前遍历中的节点
for (TreeNode<K,V> x = this, next; x != null; x = next) {
// 缓存next
next = (TreeNode<K,V>)x.next;
// 保证当前节点左右节点为null
x.left = x.right = null;
// 判断是否存在根节点
if (root == null) {
// 不存在
// 跟节点没有父级.所以设置为null
x.parent = null;
// 红黑树中,根节点是黑色的
x.red = false;
// 保存到局部变量
root = x;
}
else {
// 跟节点已确认
// 缓存key
K k = x.key;
// 缓存hash
int h = x.hash;
// key类型
Class<?> kc = null;
// -------------------- 对跟节点进行遍历,查找插入位置 --------------------
// p是插入节点的父节点
for (TreeNode<K,V> p = root;;) {
// dir - 用来表达左右.
// ph - 父节点hash
int dir, ph;
// 父节点key
K pk = p.key;
// -------------------- 判断插入到左还是右节点 --------------------
// 初始化父节点hash
// 判断父节点hash是否大于当前节点hash
if ((ph = p.hash) > h)
// dir = -1 插入节点在父节点左侧
dir = -1;
// 判断父节点hash是否小于当前节点hash
else if (ph < h)
// dir = 1 插入节点在父节点右侧
dir = 1;
// 父节点hash等于当前节点hash,进行额外的处理
// 这里使用了基于Class的一些处理办法,最终保证了dir的正确值(不为0) TODO 待补充
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
// -------------------- 获取左或右节点并进行操作 --------------------
// 缓存插入节点的父节点
TreeNode<K,V> xp = p;
// 使用dir获取父节点对应的左或右节点,并且检查这个节点是否为null.不为null时,进入下一次循环
if ((p = (dir <= 0) ? p.left : p.right) == null) {
// 父节点左或右节点为null
// 设置父级节点
x.parent = xp;
// 再次判断左右
if (dir <= 0)
// 将父节点的左子节点复制为当前节点
xp.left = x;
else
// 将父节点的右子节点复制为当前节点
xp.right = x;
// 进行平衡
root = balanceInsertion(root, x);
// 退出查找插入位置的循环,进行下一个元素的插入
break;
}
}
}
}
// 因为在进行旋转操作时,可能会修改根节点到其他节点.导致桶中的直接节点为分支节点,所以需要进行修正
moveRootToFront(tab, root);
}
以上是关于HashMap源码之treeifyBin()方法的主要内容,如果未能解决你的问题,请参考以下文章