HashMap中红黑树的定义和它内部的方法
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; //父亲节点 red-black tree links
TreeNode<K,V> left;//左子树
TreeNode<K,V> right;//右子树
TreeNode<K,V> prev; // needed, to unlink next upon deletion删除的时候使用
boolean red;//标记颜色,默认红色
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
final TreeNode<K,V> root(){//返回节点的根节点
...
}
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root){
... //把给定节点设为桶中的第一个元素
}
final TreeNode<K,V> find(int h, Object k, Class<?> kc){
... //从当前结点this开始通过给定的hash和key查找结点
}
final TreeNode<K,V> getTreeNode(int h, Object k) {
... // 从根节点开始寻找节点
}
static int tieBreakOrder(Object a, Object b) {
...//用来排序
}
final void treeify(Node<K,V>[] tab){
...//链表树化
}
final Node<K,V> untreeify(HashMap<K,V> map){
...//转化回链表
}
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v){
...//放入树节点
}
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
boolean movable) {
...//删除节点
}
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit){
...//Resize()调用
}
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p){
...//左旋 }
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p){
...//右旋
}
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,TreeNode<K,V> x) {
...//插入后保持平衡
}
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,TreeNode<K,V> x) {
...//删除后保持平衡
}
static <K,V> boolean checkInvariants(TreeNode<K,V> t) {}
转化为红黑树–treeifyBin 方法
在HashMap中put方法时候,但数组中某个位置的链表长度大于8时,会调用treeifyBin方法将链表转化为红黑树。
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// 如果桶数组table为空,或者桶数组table的长度小于MIN_TREEIFY_CAPACITY,不符合转化为红黑树的条件
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
// 数组大小小于64的,调用resize将数组大小扩容至2倍大小
resize();
// 如果符合转化为红黑树的条件,而且hash对应的桶不为null, 则重新计算 hash段位,及table的索引位,第一个节点
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
// 遍历链表,将链表元素转化成TreeNode链
TreeNode<K,V> p = replacementTreeNode(e, null);
// 确定树头节点
if (tl == null)
// TreeNode链为空,将元素设置为hd的首个节点
hd = p;
else {
// TreeNode链不为空,向TreeNode链后面添加元素
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
// 前面仅仅转换为双向链表,treeify才是转换红黑树的处理方法入口
// 让桶的第一个元素指向新建的红黑树头结点,以后这个桶里的元素就是红黑树而不是链表了
if ((tab[index] = hd) != null)
// TreeNode链表转化为红黑树
hd.treeify(tab);
}
}
构成红黑树–treeify 方法
将Treenode链转化成红黑树
final void treeify(Node<K,V>[] tab) {
// root节点
TreeNode<K,V> root = null;
// 遍历TreeNode链
for (TreeNode<K,V> x = this, next; x != null; x = next) {
// next 下一个节点
next = (TreeNode<K,V>)x.next;
// 设置左右节点为空
x.left = x.right = null;
// 第一次进入循环 root == null,确定头结点,为黑色
if (root == null) {
// 将根节点的父节点设置位空
x.parent = null;
// 将根节点设置为黑色
x.red = false;
//将x 设置为根节点
root = x;
}
// 后面进入循环走的逻辑,x 指向树中的某个节点。 此处为非根节点
else {
// 获取当前循环节点key
K k = x.key;
// 获取当前节点 hash
int h = x.hash;
Class<?> kc = null;
// 从根节点开始验证,遍历所有节点跟当前节点 x 比较,调整位置,有点像冒泡排序
for (TreeNode<K,V> p = root;;) {
// 循环查找当前节点插入的位置并添加节点
int dir, ph;
// 每个节点的 key
K pk = p.key;
// hashMap元素的hash值用来表示红黑树中节点数值大小
if ((ph = p.hash) > h)
// 当前节点值小于根节点,dir = -1 沿左路径查找
dir = -1;
else if (ph < h)
// 当前节点值大于根节点, dir = 1 沿右路径查找
dir = 1;
// 如果存在比较对象,则根据比较对象定义的comparable进行比较
// 比较之后返回查询节点路径(左或右)
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
// 当前节点的值等于根节点值。
// 如果当前节点实现Comparable接口,调用compareTo比较大小并赋值dir
// 如果当前节点没有实现Comparable接口,compareTo结果等于0,则调用tieBreakOrder继续比较大小
// tieBreakOrder本质是通过比较k与pk的hashcode
dir = tieBreakOrder(k, pk);
// 当前“根节点”赋值给xp
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
// 如果当前节点小于根节点且左子节点为空 或者 当前节点大于根节点且右子节点为空,直接添加子节点
// 将px设置为x的父节点
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
// 平衡红黑树,将二叉树转换位红黑树-正式转换红黑树
root = balanceInsertion(root, x);
// 跳出循环,继续向红黑树添加下一个元素
break;
}
}
}
}
// 确保红黑树根节点是数组中该index的第一个节点
moveRootToFront(tab, root);
}
新增元素后平衡红黑树–balanceInsertion方法
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
// 新增节点默认是红色
x.red = true;
// xp父节点 xpp祖父节点 xppl祖父左节点 xppr 祖父右节点
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
// xp = x.parent
// 如果x存在父节点,则说明目前只有一个节点,即root.根据红黑树的五大特征,根节点只能为黑色节点
if ((xp = x.parent) == null) {
// x的父节点为空,x应为根节点,应为黑色
x.red = false;
return x;
}
// xpp = xp.parent, 直接查询的是根节点
else if (!xp.red || (xpp = xp.parent) == null)
// 父节点是黑色,祖父节点为空,直接返回
return root;
// xppl = xpp.left. x的父节点是左节点时
if (xp == (xppl = xpp.left)) {
// 验证是否需要旋转
// xppr = xpp.right 存在右节点 且 右节点为红色
if ((xppr = xpp.right) != null && xppr.red) {
// 叔父节点设为黑色
xppr.red = false;
// 父节点设为黑色
xp.red = false;
// 祖父节点设置为红色
xpp.red = true;
// 将祖父节点设置为当前节点,并继续循环操作
x = xpp;
}
else {
// 叔父节点为黑色或者空
if (x == xp.right) {
// x为父节点右节点,则要进行左旋操作
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// 经过左旋x为左节点
if (xp != null) {
// 父节点涂成黑色
xp.red = false;
if (xpp != null) {
// 祖父节点不为空
// 祖父节点设为红色
xpp.red = true;
// 以租父节点为支点右旋转
root = rotateRight(root, xpp);
}
}
}
}
else {
// 验证是否需要旋转
if (xppl != null && xppl.red) {
// 将叔父节点设为黑色
xppl.red = false;
// 父节点设为黑色
xp.red = false;
// 祖父节点设为红色
xpp.red = true;
// 循环操作
x = xpp;
}
else {
if (x == xp.left) {
// 右旋
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// x为右节点
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
// 以祖父节点为支点左旋
root = rotateLeft(root, xpp);
}
}
}
}
}
}
向红黑树添加元素 – putTreeVal 方法
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
// 获取根节点
TreeNode<K,V> root = (parent != null) ? root() : this;
for (TreeNode<K,V> p = root;;) {
// 从root节点开始遍历
int dir, ph; K pk;
// 通过比较hash大小确定添加元素的位置
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
// key相同直接返回
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
// 有相同节点直接返回
if (((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
// 根据dir大小添加元素
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node<K,V> xpn = xp.next;
// 构建新的treeNode节点
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode<K,V>)xpn).prev = x;
// 平衡红黑树并保证root是index处首节点
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
}
获取方法–getTreeNode
// 获取红黑树的指定节点
final TreeNode<K,V> getTreeNode(int h, Object k) {
return ((parent != null) ? root() : this).find(h, k, null);// 从根节点开始查询
}
// 获取红黑树指定节点
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
// 此节点p就是根节点,进入循环后p代表当前节点
TreeNode<K,V> p = this;
do {
// 定义当前节点p的hash值ph、相对位置dir、key
int ph, dir; K pk;
// 获取当前节点的左子节点、右子节点
TreeNode<K,V> pl = p.left, pr = p.right, q
// 表明目标节点在当前节点的左子节点
if ((ph = p.hash) > h)
p = pl;
// 表明目标节点在当前节点的右子节点
else if (ph < h)
p = pr;
// 当前节点的hash值与目标节点hash值相等,且当前节点的key与目标key相等(equals)
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
// 当前节点的hash值与目标节点hash值相等,且当前节点的key与目标key不相等(equals)
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
// 当前节点的hash值与目标节点hash值相等,
// 且当前节点的key与目标key不相等(equals),
// 且左子节点与右子节点均不为null,目标key实现Comparable接口,且与当前节点比较不为0
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
// 当前节点的hash值与目标节点hash值相等,
// 且当前节点的key与目标key不相等(equals),
// 且左子节点与右子节点均不为null,目标key没有实现Comparable接口,
// 则直接在右子树中查询,这个方法并没有在左子树中循环,因为这是一个递归方法,
// 先遍历右子树并判断是否查找到,若无则将左子树根节点作为当前节点,不用遍历左子树依然可以覆盖全部情况
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;// 未找到,返回null
}
红黑树节点的删除–removeTreeNode方法
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
boolean movable) {
int n;
// 判断是否为空,是,直接返回
if (tab == null || (n = tab.length) == 0)
return;
// 计算下标
int index = (n - 1) & hash;
TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
// succ指向删除节点的下一个,pred指向前一个
TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
if (pred == null)
// 前一个为空,tab[index] 和 first 指向后一个节点
tab[index] = first = succ;
else
// 前一个节点不为空,则前一个节点的后一个节点指向后一个,就是删除当前节点
pred.next = succ;
if (succ != null)
// 如果后一个节点不为空,将他的前一个节点,指向当前节点的前一个
succ.prev = pred;
if (first == null) // 为空直接返回
return;
// 根节点存在父节点,说明不是根节点,调用root()获取,确保root时根节点
if (root.parent != null)
root = root.root();
// 根据节点及其左右孩子,判断当前红黑树节点的数量,进而转换为链表
if (root == null
|| (movable
&& (root.right == null
|| (rl = root.left) == null
|| rl.left == null))) {
tab[index] = first.untreeify(map); // too small
return;
}
// p 要删除的节点,replacement删除后替代他的节点
// 删除一个中级节点,但是他还有子节点,肯定连接到树上的,此时需要一个节点来顶替他的位置
TreeNode<K,V> p = this, pl = left, pr = right, replacement;
// 删除节点,左右孩子都不为空时,遍历
if (pl != null && pr != null) {
TreeNode<K,V> s = pr, sl;
// s = pr,是当前节点的右节点开始遍历,找到最后一个左子节点
// s是大于当前节点的最小值
while ((sl = s.left) != null) // find successor
s = sl;
// 颜色交换,要删除的节点,替换他的节点
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
TreeNode<K,V> sr = s.right;
TreeNode<K,V> pp = p.parent;
// 位置交换
// s == pr 意思是大于要删除节点的最小节点,就是他的右节点
// 在节点右边都是比它大的,在节点左边都是比它小的
// 在当前节点的右边,其右边节点的左边,说明大于当前节点,小于当前节点的右节点,最左边的也就是最靠近当前节点
if (s == pr) { // p was s's direct parent
p.parent = s;
s.right = p;
}
else {
// 当前节点的父节点指向,替换节点的父节点
TreeNode<K,V> sp = s.parent;
if ((p.parent = sp) != null) {
// 判断替换的节点是其父的左右节点,将替换节点的父节点的左或者右节点指向当前节点
if (s == sp.left)
sp.left = p;
else
sp.right = p;
}
// 替换节点的右节点指向删除节点的右节点,删除节点的右节点的父节点,指向替换节点
if ((s.right = pr) != null)
pr.parent = s;
}
// 删除节点的左节点置空
// 替换节点肯定不存在左节点,不然就不会成为替换节点
p.left = null;
// 但不一定没有右节点,进行右节点赋值
if ((p.right = sr) != null)
sr.parent = p;
// 将替换节点的左节点指向删除节点的左节点
if ((s.left = pl) != null)
pl.parent = s;
// 替换节点的父节点,指向删除节点的父节点,如果父节点为空,则替换节点作为根节点
if ((s.parent = pp) == null)
root = s;
// 删除节点有父节点,且为父节点的左节点,父节点的左节点指向替换节点,反之亦然
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
// 替换节点存在右节点,左节点是肯定不存在的
// replacement 为替换节点右节点,反之为删除节点
if (sr != null)
replacement = sr;
else
replacement = p;
}
// 删除节点存在左节点,replacement为左节点
else if (pl != null)
replacement = pl;
// 删除节点存在右节点,replacement为右节点
else if (pr != null)
replacement = pr;
// 左右节点都不存在,replacement为删除节点
else
replacement = p;
// replacement不为删除节点(他有子节点,或者s节点有右节点)
// 看replacement的赋值p的情况
// p节点不存在子节点
// p节点存在左右节点,且s节点无右节点
if (replacement != p) {
// 替换节点的父节点指向当前节点的父节点
TreeNode<K,V> pp = replacement.parent = p.parent;
// replacement 替换 p
// 存在左右节点是,此时的p为替换之后的节点,replacement为sr(替换p的节点的右节点)
// 存在左节点,或者右节点,那他的子节点替换p节点
if (pp == null)
// 如果父节点为null,替换节点为root节点
root = replacement;
else if (p == pp.left)
pp.left = replacement;
else
pp.right = replacement;
// 孤立p节点
p.left = p.right = p.parent = null;
}
// p节点为红色,删除后无影响,不为红色需要进行平衡
TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
// p 没有儿子或者s没有儿子,直接移除p
if (replacement == p) { // detach
TreeNode<K,V> pp = p.parent;
p.parent = null;
if (pp != null) {
if (p == pp.left)
pp.left = null;
else if (p == pp.right)
pp.right = null;
}
}
// 处理根节点
if (movable)
moveRootToFront(tab, r);
}
删除元素后的平衡红黑树–balanceDeletion
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
TreeNode<K,V> x) {
for (TreeNode<K,V> xp, xpl, xpr;;) {
// 要删除的节点是根节点,或者为null,返回根节点
if (x == null || x == root)
return root;
// x为根节点,设为黑色,返回
else if ((xp = x.parent) == null) {
x.red = false;
return x;
}
// 删除的节点是红色,无需调整
else if (x.red) {
x.red = false;
return root;
}
// 移除的节点是其父的左节点
else if ((xpl = xp.left) == x) {
// 兄弟为红色
if ((xpr = xp.right) != null && xpr.red) {
// 兄弟设为黑色
xpr.red = false;
// 父节点为红色
xp.red = true;
// 左旋
root = rotateLeft(root, xp);
// 重新将xp指向x的父节点,xpr指向xp新的右孩子
xpr = (xp = x.parent) == null ? null : xp.right;
}
// 没有兄弟节点,将x指向父节点,向上调整
if (xpr == null)
x = xp;
else {
// 存在兄弟节点,进一步调整
// xpr是兄弟节点兄弟
TreeNode<K,V> sl = xpr.left, sr = xpr.right;
// 侄子为空或者侄子为黑(没有就算黑)
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
// 兄弟节点设为红色,将x指向父节点
xpr.red = true;
x = xp;
}
// 侄子节点存在红色节点
else {
// 兄弟右节点为空或者黑子,说明兄弟左节点为红色
if (sr == null || !sr.red) {
// 兄弟左节点设置黑色
if (sl != null)
sl.red = false;
// 兄弟节点设为红色
xpr.red = true;
// 基于兄弟节点右旋
root = rotateRight(root, xpr);
// 兄弟节点重新指向
xpr = (xp = x.parent) == null ?
null : xp.right;
}
// 如果兄弟节点不为空
if (xpr != null) {
// 让兄弟节点跟父节点同时
xpr.red = (xp == null) ? false : xp.red;
// 兄弟右节点不为空,设置黑色
if ((sr = xpr.right) != null)
sr.red = false;
}
// 存在父节点
if (xp != null) {
xp.red = false;// 设置黑色
root = rotateLeft(root, xp);// 基于父节点左旋
}
x = root;
}
}
}
// x为右节点,与上面类似判断节点颜色,颜色变化与旋转
else { // symmetric
if (xpl != null && xpl.red) {
xpl.red = false;
xp.red = true;
root = rotateRight(root, xp);
xpl = (xp = x.parent) == null ? null : xp.left;
}
if (xpl == null)
x = xp;
else {
TreeNode<K,V> sl = xpl.left, sr = xpl.right;
if ((sl == null || !sl.red) &&
(sr == null || !sr.red)) {
xpl.red = true;
x = xp;
}
else {
if (sl == null || !sl.red) {
if (sr != null)
sr.red = false;
xpl.red = true;
root = rotateLeft(root, xpl);
xpl = (xp = x.parent) == null ?
null : xp.left;
}
if (xpl != null) {
xpl.red = (xp == null) ? false : xp.red;
if ((sl = xpl.left) != null)
sl.red = false;
}
if (xp != null) {
xp.red = false;
root = rotateRight(root, xp);
}
x = root;
}
}
}
}
}
HashMap对左旋右旋的实现
左旋
//root为根节点,p为旋转的结点
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,TreeNode<K,V> p) {
// r当前节点的右节点
// pp当前节点的父节点
// rl当前节点的右节点的左节点
TreeNode<K,V> r, pp, rl;
//如果p不为空且存在右子结点r
if (p != null && (r = p.right) != null) {
//判断右子结点的左子结点rl存在
if ((rl = p.right = r.left) != null)
//存在设置rl的父节点为p
rl.parent = p;
//判断p的父节点pp是否存在
if ((pp = r.parent = p.parent) == null)
//如果不存在设置新的根节点为r且黑色
(root = r).red = false;
//父结点pp存在且p为pp的左子结点
else if (pp.left == p)
pp.left = r;
else
//父结点pp存在且p为pp的左子结点
pp.right = r;
r.left = p;
p.parent = r;
}
return root;
}
右旋
//root为根节点,p为旋转的结点
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,TreeNode<K,V> p) {
// r当前节点的左节点
// pp当前节点的父节点
// rl当前节点的左节点的右节点
TreeNode<K,V> l, pp, lr;
//如果p不为空且存在左子结点l
if (p != null && (l = p.left) != null) {
//判断左子结点的右子结点lr存在
if ((lr = p.left = l.right) != null)
//存在则设置rl的父结点为p
lr.parent = p;
//判断p的父结点pp是否存在
if ((pp = l.parent = p.parent) == null)
//如果不存在设置新的根节点为l且l为黑色
(root = l).red = false;
else if (pp.right == p)
//父结点pp存在且p为pp的右子结点
pp.right = l;
else
//父结点pp存在且p为pp的左子结点
pp.left = l;
l.right = p;
p.parent = l;
}
return root;
}