内部类
存储结构
普通node
在大多数情况下都是用这个东西放在数组里的
- 就是简单的链表实现
- HashCode用的是k的hashCode和V的hashCode异或的结果
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> 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); // hashCode是用的异或操作
}
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;
}
}
TreeNode
- 这个是HashMap数据结构里最复杂的了,光是树节点的实现就占了整个HashCode1/4的内容,也负责了节点链表过长时的结构转变。
- 外部调用内容:
- split
- getTreeNode
- putTreeVal
类头
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;
-
这里能很明确的看出,1.8的实现中TreeNode就是按照红黑树的数据结构来实现的。
-
之所以是继承的LinkedHashMap中的Entry,头上的注解说的很清楚:为了能作为正常或者链式节点的扩展使用。
-
Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn extends Node) so can be used as extension of either regular or linked node. -
LinkedHashMap.entry的类头:
-
static class Entry<K,V> extends HashMap.Node<K,V> {
这样子就能很直观的看出来,构造方法中的参数意义就很明确了。
TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } -
-
结构视图
对于节点的左右顺序,可以得知的是:
- hash小的,放在左节点
- hash大的,放在右节点
- red的定义,可以参考红黑树的含义,粗浅的说大概是以下的定义链接
- 每一个节点都是红色或者黑色(red = true/false)
- 根是黑色的
- 所有叶子节点(节点不存在子节点或者为空节点被称作叶子节点)都是黑色的
- 每个红色节点必须有两个黑色的子节点。(所有路径不会出现连续的红色节点)
- 从给定节点到其后代叶子节点的每一条路径都包含相同数量的黑色节点,且没有一条路径会是别的路径长度的两倍
获取根节点
/**
* Returns root of tree containing this node.
*/
final TreeNode<K,V> root() {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
- 获取root的操作,是逐层往上的。
树结构检查
static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
tb = t.prev, tn = (TreeNode<K,V>)t.next;
//检查前后节点是否指向正确
if (tb != null && tb.next != t)
return false;
if (tn != null && tn.prev != t)
return false;
//检查当前节点是否是前驱节点的左右节点之一
if (tp != null && t != tp.left && t != tp.right)
return false;
//检查当前节点的左节点,这里能看出左节点的逻辑是:左hash小于等于头hash
if (tl != null && (tl.parent != t || tl.hash > t.hash))
return false;
//检查当前节点的左节点,这里能看出右节点的逻辑是:右hash大于等于头hash
if (tr != null && (tr.parent != t || tr.hash < t.hash))
return false;
//如果当前红,且左不空且红,且右不空且红则有问题
if (t.red && tl != null && tl.red && tr != null && tr.red)
return false;
//递归检查,树的基本操作
if (tl != null && !checkInvariants(tl))
return false;
if (tr != null && !checkInvariants(tr))
return false;
return true;
}
检查的方法能看出树结构的一些基本原则:
- 前后节点的指针必须确保前后指向正确
- 对于hash值,左<=中<=右
初始化
这个方法就很关键了,源码中链表到树结构的转化都会直接调用这个方法。
//这里需要注意:这个方法从this节点开始构造一个树
final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
//向下转型,table数组整个进行转化
//x:当前节点
for (TreeNode<K,V> x = this, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
//第一次进入循环的时候,root确实会是空的
//设置为《black》,符合定义
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<K,V> p = root;;) {
int dir, ph;
K pk = p.key;
//这个地方通过dir区分左右,先hash后Comparactor
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<K,V> xp = p;
//在这里给p赋下一次进入循环时的值,同时也是x(将要插入树结构的节点)位置的检查
//只有当下一次进入循环时的位置上是空的时候,才将x挂到树上
if ((p = (dir <= 0) ? p.left : p.right) == null) {
//先将x挂在正确(不考虑节点平衡时的情况下正确)的位置
//此处的正确的含义,指的是BST的正确
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
//节点挂载后,对整个树进行平衡处理
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}
数据结构退化
- 这里就是很简单地将数据结构从红黑树变成链表
- 阈值:6
- 之所以不用和树化阈值相同的8,变量上的解释如下
The bin count threshold for untreeifying a (split) bin during a resize operation. Should be less than TREEIFY_THRESHOLD, and at most 6 to mesh with shrinkage detection under removal.
- 有一个关键的地方:这里是通过树节点的next进行循环拼接的。
- 这个next是何时赋值的?是否会有失效的情形?
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = this; q != null; q = q.next) {
Node<K,V> p = map.replacementNode(q, null);
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}
自平衡操作
-
平衡插入
//进入到这个方法的前置条件:x已经按照BST的方法,插入到树中了 static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x) { //插入的节点预先设置为红 x.red = true; for (TreeNode<K,V> xp, xpp, xppl, xppr;;) { //如果x的前驱节点为空:说明x是根节点,不需要处理直接返回 if ((xp = x.parent) == null) { x.red = false; return x; } //如果x的前驱节点是black或x的前驱节点的前驱节点是空,就返回root(说明已经是平衡的了) else if (!xp.red || (xpp = xp.parent) == null)//-------------note0 return root; //如果x的前驱节点,是x的前驱节点的前驱节点的左树的根节点 if (xp == (xppl = xpp.left)) { //如果xpp的右节点不为空且是红 if ((xppr = xpp.right) != null && xppr.red) { //这里就是遵守红黑树的定义之一: // 路径上不能出现2个连续的红节点 //这里是重染色 xppr.red = false; xp.red = false; xpp.red = true; //回到祖父节点进行分析:因为当前的红黑排序是满足约束的 x = xpp; } //这里和上面就出现了不同的分歧:“叔叔”节点黑的父亲节点是红的 else {//--------------------------note 2 //如果当前节点是父节点的右节点 if (x == xp.right) { //这里左旋的原因是:p和pp都是red节点 //xp是red在0中已经判断了,p是红的已经在一开始就确定了 --------note1 root = rotateLeft(root, x = xp); //因为x已经左旋了,需要保持在相应的层继续进行操作 //因此做了这个操作把指针复原(此时,原来的x.val在xp的位置了) //复原后,依然是x->xp->xpp的结构 xpp = (xp = x.parent) == null ? null : xp.parent; } //如果执行了上一步,这里是一定执行的 if (xp != null) { //根据1可以判断,x,xp都是红色的 //而且左旋中,在非特殊情况下并不给x,xp染色 xp.red = false; if (xpp != null) { //祖父节点染红 xpp.red = true; //根据祖父节点去右旋 root = rotateRight(root, xpp); } } } } //这边就是xp为xppr(xp是右边)或xp为空的情况 //和上面是一样的操作 else { if (xppl != null && xppl.red) { xppl.red = false; xp.red = false; xpp.red = true; x = xpp; } else {//和note2一样的流程 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); } } } } } } -
自平衡删除
- 执行这个方法的前提,是树结构中已经删除了某个节点
- 此处的x,是删除节点自身或替代节点最终出现位置的节点,并且已经替换完毕
- 根据前面的删除树节点步骤可知,只有当节点左右都为空时,此处的x才是已删除的节点的值
- 返回的值是树的根节点
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
TreeNode<K,V> x) {
/**
* xp
* / \
* xpl xpr
* / \
* sl sr
*/
for (TreeNode<K,V> xp, xpl, xpr;;) {
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) {
//如果节点和兄弟节点颜色不同 - 节点颜色swap,根据自平衡操作
//进行左旋(右边的节点有问题因此左旋)
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<K,V> sl = xpr.left, sr = xpr.right;
//这里处理xpr的颜色相关:如果x的左右节点都为黑(为空或不为红),则处理
//然后往上溯源继续分析
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
xpr.red = true;
x = xp;
}
else {
if (sr == null || !sr.red) {//如果右边的节点,是黑色节点
if (sl != null)
//需要保持两个节点都是黑的,因此需要确保red为false
sl.red = false;
//把父节点变红(确保右旋后,红黑顺序没有问题),随后右旋
xpr.red = true;
root = rotateRight(root, xpr);
/**右旋后未指定xp时:
* xp
* / | \
* xpl x sl
* \
* xpr
* \
* sr
*/
//右旋后重确定xp,xpr指向的节点
xpr = (xp = x.parent) == null ?
null : xp.right;
/**resign:
* xp
* / | \
* xpl=x xpr
* \
* ?
* \
* sr[ori]
*/
}
/**resign:
* xp
* / | \
* xpl=x xpr
* \
* sr
* \
* ?
*/
if (xpr != null) {
xpr.red = (xp == null) ? false : xp.red;
if ((sr = xpr.right) != null)
sr.red = false;
}
/**
* xpr
* \
* xp
* / \
* xpl sr
回到根节点进行分析
*/
if (xp != null) {
xp.red = false;
root = rotateLeft(root, xp);
}
x = root;
}
}
}
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;
}
}
}
}
}
- 左旋
A(pp) A(pp)
| |
B(p) --------- >>>> C(r)
\ /
C(r) B(p)
/ \
D(RL) D(rl)
左旋就是将原本的右侧子节点作为父节点,左旋就意味着将旋转的节点变成了左节点。参考
也可以这么说:左旋就是把父节点,放到下一层的左子节点
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null)
rl.parent = p;
if ((pp = r.parent = p.parent) == null)
(root = r).red = false;
else if (pp.left == p)
pp.left = r;
else
pp.right = r;
r.left = p;
p.parent = r;
}
return root;
}
- 右旋
pp pp
| |
P --------------》 L
/ \
L P
\ /
LR LR
类似上面的,右转就是把当前节点和左子节点地位互换,其他地方的结构不变。
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null)
rl.parent = p;
if ((pp = r.parent = p.parent) == null)
(root = r).red = false;
else if (pp.left == p)
pp.left = r;
else
pp.right = r;
r.left = p;
p.parent = r;
}
return root;
}
值插入
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;;) {
int dir, ph; K pk;
//方向查找 ------------ 1
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;
//这里跟1目的相同,获取比较的dir,只有当无法通过hash和Comparactor进行比较时才会进行下面的代码块
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获取的位置为null)才能进行插入工作
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node<K,V> xpn = xp.next;
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
//这里不太清楚的是:为什么在链表结构的部分中,是插在xp和xp之前的next之间
//自己的解释:next后面其实可能还有next,因此直接插在中间比较合适(方便)
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode<K,V>)xpn).prev = x;
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
}
树节点移除
- 方法声明上,明确说明:调用这个方法之前,预删除节点需要已经存在于map中。
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;
TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
//pred是空,说明现在的节点,是当前bin内的第一个节点
//因此把bin的第一个节点设置成当前节点的next节点,链表节点上当前节点就删掉了
if (pred == null)//--------------------------- 1 start
tab[index] = first = succ;
else
pred.next = succ;
if (succ != null)
succ.prev = pred;//------------------------ 1 end
if (first == null)
return;
//往下是处理树结构的,root->first
if (root.parent != null)
root = root.root();
//这里为什么只判断到了左子树的左子节点?
/**
* O
* / \
O O
* \ / \
1 1 1
其实并不是没有判断,而是:只要三层结构中,少了任意一个节点,目前的树结构就肯定只 有最多6个节点,小于等于树退化阈值(6),因此在此退化成链表,判断左边的和右边的4 个节点中任意一个都可
*/
if (root == null || root.right == null ||
(rl = root.left) == null || rl.left == null) {
/**
这里需要注意一下
1.untreeify不删除节点,并且是通过next指针去进行转化的
2.节点的删除,在1中已经进行过了
**/
tab[index] = first.untreeify(map); // too small
return;
}
//下面的就是开始删除树结构上的节点了
//链表上(next,prev指针)删干净了,但是树结构上的是没有动的
TreeNode<K,V> p = this, pl = left, pr = right, replacement;
if (pl != null && pr != null) {
TreeNode<K,V> s = pr, sl;
//找到右子树最下左节点S,此处的操作和BST是相同的:就是找右子树的最小节点
while ((sl = s.left) != null) // find successor
s = sl;
//交换p和p右子节点的颜色
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;
//2 - s和p交换位置
if (s == pr) { // ----------------2 start
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;
}
// ------------------------- 2 end 3 start
//3 . 处理p和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;
}
else if (pl != null)
replacement = pl;
else if (pr != null)
replacement = pr;
else
replacement = p;
//给replacement赋值,replacement-用来替换p《处理后位置(针对上面pl,pr都非空的情况)》
if (replacement != p) {
TreeNode<K,V> 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;
}
//此处注意:只有p非红的情况才会触发平衡删除后整理的流程
TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
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);
}
重组split
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
TreeNode<K,V> b = this;
// Relink into lo and hi lists, preserving order
TreeNode<K,V> loHead = null, loTail = null;
TreeNode<K,V> hiHead = null, hiTail = null;
int lc = 0, hc = 0;
for (TreeNode<K,V> e = b, next; e != null; e = next) {
next = (TreeNode<K,V>)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);
}
}
}
- 这里可以看到,这里使用的hash方式和链表的hash方式是一样的:
- oldCap & hash
- 也同样的是拼成lo和hi两条链表
- 然后再根据lo和hi的长度,决定结构(链表或RB树)
- 结构决定后,直接赋值到指定的位置上
- QA:为什么使用了:hiHead.untreeify?(todo)
节点视图
节点上移
-
这个方法的使用情景:
- 确定了节点是(将要是)根节点的情况
-
如果头节点当前不是根节点,那么
-
将root节点从当前位置摘下
- root.prev.next - > next
- root.next.prev - > prev
-
将first(数组该hash位置的头节点)进行处理
- first.prev - > root , root.next - > first
- first.prev - > null
-
Q:不需要重新排序?
-
检查整体树结构是否依然是红黑树的结构
-
/**
* Ensures that the given root is the first node of its bin.
*/
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
int n;
if (root != null && tab != null && (n = tab.length) > 0) {
int index = (n - 1) & root.hash;
TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
if (root != first) {
Node<K,V> rn;
tab[index] = root;
TreeNode<K,V> rp = root.prev;
if ((rn = root.next) != null)//inline-inject
((TreeNode<K,V>)rn).prev = rp;
if (rp != null)
rp.next = rn;
if (first != null)
first.prev = root;
root.next = first;
root.prev = null;
}
assert checkInvariants(root);
}
}
节点查找
/**
* Finds the node starting at root p with the given hash and key.
* The kc argument caches comparableClassFor(key) upon first use
* comparing keys.
*/
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk;
TreeNode<K,V> pl = p.left, pr = p.right, q;
//这里能看出来,是根据hash的大小,进行查找的
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;
//这里比较的情况是:如果节点的hash值相等,需要根据key类的comparator进行比较
//大小顺序情况和hash值是相同的
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
//这里的情况是:comparator的结果是0,就是相等的情况
//注意这里是右节点进行递归查找的,左节点就直接往下走循环了
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;
}
final TreeNode<K,V> getTreeNode(int h, Object k) {
return ((parent != null) ? root() : this).find(h, k, null);
}
相对值提取
-
如果hashCode相等,并且不是Comparable,就调用这个方法
static int tieBreakOrder(Object a, Object b) { int d; if (a == null || b == null || (d = a.getClass().getName(). compareTo(b.getClass().getName())) == 0) //native方法 d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); return d; }
内容视图结构
KeySet
-
其实就是一个简单的keySet的实现而已。
-
一贯的套路:
-
通过modCount实现了fail-fast机制。
-
remove等操作是在原数据结构上实现的
- 自身是原数据结构的部分直接映射
- 任何修改都可能会影响到原数据结构
这一点和SubList是相同的。
-
-
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
Values
- 跟上面的一样,不再赘述。
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
EntrySet
- 这三兄弟其实都是马甲。
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
迭代结构
HashIterator - 爹
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
键,值,键值对 - 三个懒儿子
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
final class ValueIterator extends HashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}