微信公衆號:I am CR7
若有問題或建議,請在下方留言;
最近更新:2018-09-18java
繼上一篇HashMap之元素插入,咱們繼續來看下元素刪除的實現原理。node
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}
複製代碼
由於HashMap存在三種存儲方式,數組、單鏈表、紅黑樹,那麼刪除元素時必然存在着這三種狀況。其中,紅黑樹的刪除最爲複雜,我們接着往下看。微信
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}
複製代碼
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}
複製代碼
以上爲HashMap的紅黑樹刪除流程,其實思路和TreeMap中紅黑樹大體相同,關於TreeMap的解析,請看TreeMap之元素刪除,文章中我對紅黑樹的刪除過程進行了詳細的分析,這裏就不作詳細闡述了。app
咱們經過一個具體的例子,來體會下刪除的過程,請看:
this
經過上述的分析,結合着TreeMap之元素刪除這篇文章,我想,要理解HashMap的刪除,並非一件難事。
文章的最後,感謝你們的支持,歡迎掃描下方二維碼,進行關注。若有任何疑問,歡迎你們留言。spa