HashMap之TreeNode
先看一下简单的Node单向链表,然后再看复杂一点的TreeNode
static class Node
implements Map.Entry
{ final int hash; final K key; V value; Node
next; Node(int hash, K key, V value, Node
next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry
e = (Map.Entry
)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } }
很简单,一个Node相当于链表的一个节点,next属性对应着下一个节点
- 每个节点或是红色,或是黑色的
- 跟节点是黑色的
- 每个叶节点(NIL)是黑色的
- 如果一个节点是红色的,则它的两个子节点都是黑色的
- 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数码的黑节点
如图

叶节点(NIL)不包含任何数据,只是一个标记,没有实际意义
下面通过红黑树的插入、删除、查找、左旋、右旋进一步了解TreeNode
先看一下构造
TreeNode(int hash, K key, V val, Node
next) { super(hash, key, val, next); }
可以看到调用了父类的构造方法,也就是说,TreeNode是红黑树的同时也是一个单项链表
- 使P成为根节点并变成黑色满足性质2
- G成为P的右节点
- P的右节点成为G的左节点
static
TreeNode
rotateRight(TreeNode
root, TreeNode
p) { TreeNode
l, pp, lr; if (p != null && (l = p.left) != null) { if ((lr = p.left = l.right) != null) lr.parent = p; if ((pp = l.parent = p.parent) == null) (root = l).red = false; else if (pp.right == p) pp.right = l; else pp.left = l; l.right = p; p.parent = l; } return root; }
final TreeNode
find(int h, Object k, Class
kc) { TreeNode
p = this; do { int ph, dir;K pk; TreeNode
pl = p.left, pr = p.right, q; if ((ph = p.hash) > h) p = pl; else if (ph < h) p = pr; else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; else if (pl == null) p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr; else if ((q = pr.find(h, k, kc)) != null) return q; else p = pl; } while (p != null); return null; }
分3点来说find方法
1.find方法根据当前节点的hash与参数的h比较,如果大于pl赋值给p,如果小于pr赋值给p,相等的情况通过当前节点的key与参数的key比较,不相等在通过equals方法比较。
2.接下来在判断如果pl等于null把br赋值给p,pr等于null的话吧pl赋值给p,都不等于空的话在根据key实现的Comparable比较。comparableClassFor方法是获取k的运行时类型,compareComparables方法先判断,key与运行时kc是同类型,在通过调用k和kc实现的Comparable接口的compareTo进行比较
static int compareComparables(Class
kc, Object k, Object x) { return (x == null || x.getClass() != kc ? 0 : ((Comparable)k).compareTo(x)); }
3.以上这些条件如果都不满足,那右节点通过递归比较找到则返回,找不到,把左节点赋值给p,进入下次循环在比较
final TreeNode
putTreeVal(HashMap
map, Node
[] tab, int h, K k, V v) { Class
kc = null; boolean searched = false; TreeNode
root = (parent != null) ? root() : this;
final TreeNode
root() { for (TreeNode
r = this, p; ; ) { if ((p = r.parent) == null) return r; r = p; } }
就剩下一个for循环了
for (TreeNode
p = root;;) { int dir, ph; K pk; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) { if (!searched) { TreeNode
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); }
static int tieBreakOrder(Object a, Object b) { int d; if (a == null || b == null || (d = a.getClass().getName(). compareTo(b.getClass().getName())) == 0) d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); return d; }
TreeNode
xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { Node
xpn = xp.next; TreeNode
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
)xpn).prev = x; moveRootToFront(tab, balanceInsertion(root, x)); return null; }
TreeNode
newTreeNode(int hash, K key, V value, Node
next) { return new TreeNode<>(hash, key, value, next); }
没什么特别的就是创建一个TreeNode
static
TreeNode
balanceInsertion(TreeNode
root, TreeNode
x) { x.red = true; for (TreeNode
xp, xpp, xppl, xppr;;) { if ((xp = x.parent) == null) { x.red = false; return x; } else if (!xp.red || (xpp = xp.parent) == null) return root; if (xp == (xppl = xpp.left)) { if ((xppr = xpp.right) != null && xppr.red) { xppr.red = false; xp.red = false; xpp.red = true; x = xpp; } else { if (x == xp.right) { root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } 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; } if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateLeft(root, xpp); } } } } } }
好了,现在在来看moveRootToFront方法
static
void moveRootToFront(Node
[] tab, TreeNode
root) { int n; if (root != null && tab != null && (n = tab.length) > 0) { int index = (n - 1) & root.hash; TreeNode
first = (TreeNode
)tab[index]; if (root != first) { Node
rn; tab[index] = root; TreeNode
rp = root.prev; if ((rn = root.next) != null) ((TreeNode
)rn).prev = rp; if (rp != null) rp.next = rn; if (first != null) first.prev = root; root.next = first; root.prev = null; } assert checkInvariants(root); } }
上面解释了tab参数,第5行取出经过balanceInsertion摧残之后的根节点,第6行判断如果与摧残之前的节点不同,通过7-17的代码调整链表顺序
删除
final void removeTreeNode(HashMap
map, Node
[] tab, boolean movable) { int n; if (tab == null || (n = tab.length) == 0) return; int index = (n - 1) & hash; TreeNode
first = (TreeNode
)tab[index], root = first, rl; TreeNode
succ = (TreeNode
)next, pred = prev; if (pred == null) tab[index] = first = succ; else pred.next = succ; if (succ != null) succ.prev = pred; if (first == null) return; if (root.parent != null) root = root.root(); if (root == null || root.right == null || (rl = root.left) == null || rl.left == null) { tab[index] = first.untreeify(map); // too small return; }
TreeNode
p = this, pl = left, pr = right, replacement; if (pl != null && pr != null) { TreeNode
s = pr, sl; while ((sl = s.left) != null) // find successor s = sl; boolean c = s.red; s.red = p.red; p.red = c; // swap colors TreeNode
sr = s.right; TreeNode
pp = p.parent; if (s == pr) { // p was s's direct parent p.parent = s; s.right = p; } else { TreeNode
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; if (sr != null) replacement = sr; else replacement = p; }
处理完左右节点都不为空的情况,接下来该处理,左节点不为null右节点为null、右节点不为null左节点为null、左右都为null
else if (pl != null) replacement = pl; else if (pr != null) replacement = pr; else replacement = p;
if (replacement != p) { TreeNode
pp = replacement.parent = p.parent; if (pp == null) root = replacement; else if (p == pp.left) pp.left = replacement; else pp.right = replacement; p.left = p.right = p.parent = null; } TreeNode
r = p.red ? root : balanceDeletion(root, replacement);
这里删除节点通过判断replacement != p,那么如果replacement == p
if (replacement == p) { // detach TreeNode
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); }
简单:如果replacement现在是跟节点黑加黑,什么也不做。或者当前是红黑色,只需要把当前节点染成黑色,就恢复了红黑树的性质
static
TreeNode
balanceDeletion(TreeNode
root, TreeNode
x) { for (TreeNode
xp, xpl, xpr;;) { if (x == null || x == root) return root; 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); xpr = (xp = x.parent) == null ? null : xp.right; } if (xpr == null) x = xp; else { TreeNode
sl = xpr.left, sr = xpr.right; if ((sr == null || !sr.red) && (sl == null || !sl.red)) { 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; } } } else { // symmetric
TreeNode与红黑树相关的操作就这些了,旋转、查找、出入、删除。为了满足HashMap重置内部数组大小,还有一个拆分方法。当HashMap需要扩容时候,就会调用拆分方法,把红黑树拆分成两个链表,如果两个链表大于一定的值,那么就还转成红黑树,否则就转换成链表
拆分
final void split(HashMap
map, Node
[] tab, int index, int bit) { TreeNode
b = this; // Relink into lo and hi lists, preserving order TreeNode
loHead = null, loTail = null; TreeNode
hiHead = null, hiTail = null; int lc = 0, hc = 0; for (TreeNode
e = b, next; e != null; e = next) { next = (TreeNode
)e.next; e.next = null; if ((e.hash & bit) == 0) { if ((e.prev = loTail) == null) loHead = e; else loTail.next = e; loTail = e; ++lc; } else { if ((e.prev = hiTail) == null) hiHead = e; else hiTail.next = e; hiTail = e; ++hc; } } if (loHead != null) { if (lc <= UNTREEIFY_THRESHOLD) tab[index] = loHead.untreeify(map); else { tab[index] = loHead; if (hiHead != null) // (else is already treeified) loHead.treeify(tab); } } if (hiHead != null) { if (hc <= UNTREEIFY_THRESHOLD) tab[index + bit] = hiHead.untreeify(map); else { tab[index + bit] = hiHead; if (loHead != null) hiHead.treeify(tab); } } }
static final int UNTREEIFY_THRESHOLD = 6;
final void treeify(Node
[] tab) { TreeNode
root = null; for (TreeNode
x = this, next; x != null; x = next) { next = (TreeNode
)x.next; x.left = x.right = null; if (root == null) { x.parent = null; x.red = false; root = x; } else { K k = x.key; int h = x.hash; Class
kc = null; for (TreeNode
p = root;;) { int dir, ph; K pk = p.key; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); TreeNode
xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { x.parent = xp; if (dir <= 0) xp.left = x; else xp.right = x; root = balanceInsertion(root, x); break; } } } } moveRootToFront(tab, root); }
inal Node
untreeify(HashMap
map) { Node
hd = null, tl = null; for (Node
q = this; q != null; q = q.next) { Node
p = map.replacementNode(q, null); if (tl == null) hd = p; else tl.next = p; tl = p; } return hd; }
到这里TreeNode就说完了,这样下一篇看HashMap分析的时候就会轻松很多了
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/202542.html原文链接:https://javaforall.net
