微信公眾號:I am CR7
如有問題或建議,請在下方留言;
最近更新:2018-09-18
HashMap之元素刪除
繼上一篇HashMap之元素插入,我們繼續來看下元素刪除的實現原理。
1、原始碼:
1public V remove(Object key) {
2 Node<K,V> e;
3 return (e = removeNode(hash(key), key, null, false, true)) == null ?
4 null : e.value;
5}
複製程式碼
看下核心方法removeNode:
1//matchValue為false 表示不需要比對value值一致
2//movable為false 表示刪除節點後不移動其他節點
3final Node<K,V> removeNode(int hash, Object key, Object value,
4 boolean matchValue, boolean movable) {
5 //p為當前檢查的節點
6 Node<K,V>[] tab; Node<K,V> p; int n, index;
7 if ((tab = table) != null && (n = tab.length) > 0 &&
8 (p = tab[index = (n - 1) & hash]) != null) { //待刪除節點在陣列索引位置存在元素
9 //node為找到的刪除節點
10 Node<K,V> node = null, e; K k; V v;
11 if (p.hash == hash &&
12 ((k = p.key) == key || (key != null && key.equals(k))))//雜湊值一致,key一致則找到了要刪除的節點
13 node = p;
14 else if ((e = p.next) != null) {//未找到則看後繼節點
15 if (p instanceof TreeNode)//如果後繼節點為紅黑樹節點,則在紅黑樹中查詢要刪除的節點
16 node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
17 else {
18 do {
19 if (e.hash == hash &&
20 ((k = e.key) == key ||
21 (key != null && key.equals(k)))) {
22 node = e;
23 break;
24 }
25 p = e;
26 } while ((e = e.next) != null);//不為紅黑樹節點,則遍歷單連結串列查詢
27 }
28 }
29 if (node != null && (!matchValue || (v = node.value) == value ||
30 (value != null && value.equals(v)))) {//找到節點,matchValue為true,還需要比對value值
31 if (node instanceof TreeNode)
32 ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);//待刪除節點為紅黑樹節點,則進行紅黑樹節點的刪除操作
33 else if (node == p)//待刪除節點為陣列中的元素,直接將後繼節點替換即可
34 tab[index] = node.next;
35 else//待刪除節點為單連結串列中的元素,將後繼節點作為前驅節點的後繼節點即可
36 p.next = node.next;
37 ++modCount;
38 --size;
39 afterNodeRemoval(node);
40 return node;
41 }
42 }
43 return null;
44}
複製程式碼
2、流程圖:
3、說明:
因為HashMap存在三種儲存方式,陣列、單連結串列、紅黑樹,那麼刪除元素時必然存在著這三種情況。其中,紅黑樹的刪除最為複雜,我們們接著往下看。
紅黑樹之查詢元素
1、原始碼:
1final TreeNode<K,V> getTreeNode(int h, Object k) {
2 return ((parent != null) ? root() : this).find(h, k, null);
3}
4
5//查詢紅黑樹的根節點
6final TreeNode<K,V> root() {
7 for (TreeNode<K,V> r = this, p;;) {
8 if ((p = r.parent) == null)
9 return r;
10 r = p;
11 }
12}
13
14//遍歷紅黑樹查詢指定雜湊和key的節點
15final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
16 TreeNode<K,V> p = this;
17 do {
18 int ph, dir; K pk;
19 TreeNode<K,V> pl = p.left, pr = p.right, q;//儲存左節點 右節點
20 if ((ph = p.hash) > h) //左節點雜湊值大於給定查詢節點的雜湊值,則繼續往左找
21 p = pl;
22 else if (ph < h)//左節點雜湊值小於給定查詢節點的雜湊值,則往右找
23 p = pr;
24 else if ((pk = p.key) == k || (k != null && k.equals(pk)))//當前節點key值一致,則返回該節點
25 return p;
26 else if (pl == null)//左節點為空,則往右找
27 p = pr;
28 else if (pr == null)//右節點為空,則往左找
29 p = pl;
30 else if ((kc != null ||//雜湊相同,key不同,且有左右節點。此時看key是否可比較,是則比較key值
31 (kc = comparableClassFor(k)) != null) &&
32 (dir = compareComparables(kc, k, pk)) != 0)
33 p = (dir < 0) ? pl : pr;//小於往左,大於往右
34 else if ((q = pr.find(h, k, kc)) != null)//雜湊相同,key不可比或者key也相同,則往右查詢
35 return q;
36 else//否則往左
37 p = pl;
38 } while (p != null);
39 return null;
40}
複製程式碼
2、流程圖:
紅黑樹之刪除元素
1、原始碼:
1final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
2 boolean movable) {
3 int n;
4 if (tab == null || (n = tab.length) == 0)
5 return;
6 int index = (n - 1) & hash;
7 TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
8 TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
9 if (pred == null) //待刪除節點為根節點,則其後繼節點作為陣列索引位置的元素
10 tab[index] = first = succ;
11 else//待刪除節點存在前驅節點,則後繼節點作為前驅節點的下一個節點
12 pred.next = succ;
13 if (succ != null)//待刪除節點存在後繼節點,則前驅節點作為後繼節點的上一個節點
14 succ.prev = pred;
15 if (first == null)//陣列索引位置元素為null,直接返回
16 return;
17 if (root.parent != null)//找到紅黑樹的根節點
18 root = root.root();
19 if (root == null || root.right == null ||
20 (rl = root.left) == null || rl.left == null) {//紅黑樹太小則進行去樹化操作
21 tab[index] = first.untreeify(map); // too small
22 return;
23 }
24 //查詢替代節點replacement
25 //p為待刪除節點,pl為其左節點,pr為其右節點,replacement為替代節點
26 TreeNode<K,V> p = this, pl = left, pr = right, replacement;
27 if (pl != null && pr != null) {//待刪除節點有左右節點
28 //s為後繼節點
29TreeNode<K,V> s = pr, sl;
30 while ((sl = s.left) != null)//往待刪除節點右子樹的左邊走
31 s = sl;
32 boolean c = s.red; s.red = p.red; p.red = c;//互換後繼節點和待刪除節點的顏色
33 //sr為後繼節點的右節點
34 TreeNode<K,V> sr = s.right;
35 //pp為待刪除節點的父節點
36 TreeNode<K,V> pp = p.parent;
37 if (s == pr) {//待刪除節點的右節點無左孩子--->右節點和待刪除節點互換
38 p.parent = s;
39 s.right = p;
40 }
41 else {//待刪除節點的右節點有左孩子
42 //sp為後繼節點的父節點
43 TreeNode<K,V> sp = s.parent;
44 //後繼節點存在父節點,則讓待刪除節點替代後繼節點
45 if ((p.parent = sp) != null) {//後繼節點的父節點成為待刪除節點的父節點
46 if (s == sp.left)//後繼節點為其父節點的左孩子
47 sp.left = p;//待刪除節點就作為後繼節點的父節點的左孩子
48 else
49 sp.right = p;//待刪除節點就作為後繼節點的父節點的右孩子
50 }
51 //待刪除節點存在右節點,則讓後繼節點成為其父節點
52 if ((s.right = pr) != null)
53 pr.parent = s;
54 }
55 p.left = null;//待刪除節點左孩子為null
56 if ((p.right = sr) != null)//後繼節點存在右節點,則讓其成為待刪除節點的右節點
57 sr.parent = p;//相對應,待刪除節點成為其父節點
58 if ((s.left = pl) != null)//待刪除節點存在左節點,則讓其成為後繼節點的左節點
59 pl.parent = s;//相對應,後繼節點成為其父節點
60 //待刪除節點存在父節點,則讓後繼節點替代待刪除節點
61 if ((s.parent = pp) == null)//待刪除節點不存在父節點,則後繼節點父節點為null
62 root = s;//後繼節點成為根節點
63 else if (p == pp.left)//待刪除節點存在父節點,且待刪除節點是其左節點
64 pp.left = s;//後繼節點作為其左節點
65 else
66 pp.right = s;//後繼節點作為其右節點
67 //後繼節點存在右節點,則替代節點為該節點
68 if (sr != null)
69 replacement = sr;
70 else //替代節點為待刪除節點(等於未找到)
71 replacement = p;
72 }
73 else if (pl != null)//待刪除節點只有左節點
74 replacement = pl;
75 else if (pr != null)//待刪除節點只有右節點
76 replacement = pr;
77 else//待刪除節點為葉子節點
78 replacement = p;
79 if (replacement != p) {//替代節點不為待刪除節點,則先進行節點刪除,然後進行平衡調整
80 TreeNode<K,V> pp = replacement.parent = p.parent;
81 if (pp == null)
82 root = replacement;
83 else if (p == pp.left)
84 pp.left = replacement;
85 else
86 pp.right = replacement;
87 p.left = p.right = p.parent = null;
88 }
89
90 TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);//進行平衡調整
91
92 if (replacement == p) { //替代節點為待刪除節點,則先進行平衡調整,然後進行節點刪除
93 TreeNode<K,V> pp = p.parent;
94 p.parent = null;
95 if (pp != null) {
96 if (p == pp.left)
97 pp.left = null;
98 else if (p == pp.right)
99 pp.right = null;
100 }
101 }
102 if (movable)//將紅黑樹根節點移動到陣列索引位置
103 moveRootToFront(tab, r);
104}
複製程式碼
2、流程圖:
3、說明:
以上為HashMap的紅黑樹刪除流程,其實思路和TreeMap中紅黑樹大致相同,關於TreeMap的解析,請看TreeMap之元素刪除,文章中我對紅黑樹的刪除過程進行了詳細的分析,這裡就不做詳細闡述了。
示例
我們通過一個具體的例子,來體會下刪除的過程,請看:
總結
通過上述的分析,結合著TreeMap之元素刪除這篇文章,我想,要理解HashMap的刪除,並不是一件難事。
文章的最後,感謝大家的支援,歡迎掃描下方二維碼,進行關注。如有任何疑問,歡迎大家留言。