摘要:介紹跳表是一個隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),實(shí)質(zhì)就是一種可以進(jìn)行二分查找的有序鏈表。跳表不僅能提高搜索性能,同時(shí)也可以提高插入和刪除操作的性能。源碼分析主要內(nèi)部類內(nèi)部類跟存儲結(jié)構(gòu)結(jié)合著來看,大概能預(yù)測到代碼的組織方式。
介紹
跳表是一個隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),實(shí)質(zhì)就是一種可以進(jìn)行二分查找的有序鏈表。
跳表在原有的有序鏈表上面增加了多級索引,通過索引來實(shí)現(xiàn)快速查找。
跳表不僅能提高搜索性能,同時(shí)也可以提高插入和刪除操作的性能。
存儲結(jié)構(gòu)跳表在原有的有序鏈表上面增加了多級索引,通過索引來實(shí)現(xiàn)快速查找。
源碼分析 主要內(nèi)部類內(nèi)部類跟存儲結(jié)構(gòu)結(jié)合著來看,大概能預(yù)測到代碼的組織方式。
// 數(shù)據(jù)節(jié)點(diǎn),典型的單鏈表結(jié)構(gòu) static final class Node{ final K key; // 注意:這里value的類型是Object,而不是V // 在刪除元素的時(shí)候value會指向當(dāng)前元素本身 volatile Object value; volatile Node next; Node(K key, Object value, Node next) { this.key = key; this.value = value; this.next = next; } Node(Node next) { this.key = null; this.value = this; // 當(dāng)前元素本身(marker) this.next = next; } } // 索引節(jié)點(diǎn),存儲著對應(yīng)的node值,及向下和向右的索引指針 static class Index { final Node node; final Index down; volatile Index right; Index(Node node, Index down, Index right) { this.node = node; this.down = down; this.right = right; } } // 頭索引節(jié)點(diǎn),繼承自Index,并擴(kuò)展一個level字段,用于記錄索引的層級 static final class HeadIndex extends Index { final int level; HeadIndex(Node node, Index down, Index right, int level) { super(node, down, right); this.level = level; } }
(1)Node,數(shù)據(jù)節(jié)點(diǎn),存儲數(shù)據(jù)的節(jié)點(diǎn),典型的單鏈表結(jié)構(gòu);
(2)Index,索引節(jié)點(diǎn),存儲著對應(yīng)的node值,及向下和向右的索引指針;
(3)HeadIndex,頭索引節(jié)點(diǎn),繼承自Index,并擴(kuò)展一個level字段,用于記錄索引的層級;
構(gòu)造方法public ConcurrentSkipListMap() { this.comparator = null; initialize(); } public ConcurrentSkipListMap(Comparator super K> comparator) { this.comparator = comparator; initialize(); } public ConcurrentSkipListMap(Map extends K, ? extends V> m) { this.comparator = null; initialize(); putAll(m); } public ConcurrentSkipListMap(SortedMapm) { this.comparator = m.comparator(); initialize(); buildFromSorted(m); } ##四個構(gòu)造方法里面都調(diào)用了initialize()這個方法 private static final Object BASE_HEADER = new Object(); private void initialize() { keySet = null; entrySet = null; values = null; descendingMap = null; // Node(K key, Object value, Node next) // HeadIndex(Node node, Index down, Index right, int level) head = new HeadIndex (new Node (null, BASE_HEADER, null), null, null, 1); }
可以看到,這里初始化了一些屬性,并創(chuàng)建了一個頭索引節(jié)點(diǎn),里面存儲著一個數(shù)據(jù)節(jié)點(diǎn),這個數(shù)據(jù)節(jié)點(diǎn)的值是空對象,且它的層級是1。
所以,初始化的時(shí)候,跳表中只有一個頭索引節(jié)點(diǎn),層級是1,數(shù)據(jù)節(jié)點(diǎn)是一個空對象,down和right都是null。
通過內(nèi)部類的結(jié)構(gòu)我們知道,一個頭索引指針包含node, down, right三個指針,為了便于理解,我們把指向node的指針用虛線表示,其它兩個用實(shí)線表示,也就是虛線不是表明方向的。
增加元素我們知道跳表插入元素的時(shí)候,會隨機(jī)的方式?jīng)Q定出它需要的層級,然后向下找到各層鏈中它所在的位置,最后通過單鏈表插入的方式把節(jié)點(diǎn)及索引插入進(jìn)去來實(shí)現(xiàn)的。
public V put(K key, V value) { // 不能存儲value為null的元素 // 因?yàn)関alue為null標(biāo)記該元素被刪除(后面會看到) if (value == null) throw new NullPointerException(); // 調(diào)用doPut()方法添加元素 return doPut(key, value, false); } private V doPut(K key, V value, boolean onlyIfAbsent) { // 添加元素后存儲在z中 Nodez; // added node // key也不能為null if (key == null) throw new NullPointerException(); Comparator super K> cmp = comparator; // Part I:找到目標(biāo)節(jié)點(diǎn)的位置并插入 // 這里的目標(biāo)節(jié)點(diǎn)是數(shù)據(jù)節(jié)點(diǎn),也就是最底層的那條鏈 // 自旋 outer: for (;;) { // 尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn),存儲在b中,b=before // 并把b的下一個數(shù)據(jù)節(jié)點(diǎn)存儲在n中,n=next // 為了便于描述,我這里把b叫做當(dāng)前節(jié)點(diǎn),n叫做下一個節(jié)點(diǎn) for (Node b = findPredecessor(key, cmp), n = b.next;;) { // 如果下一個節(jié)點(diǎn)不為空 // 就拿其key與目標(biāo)節(jié)點(diǎn)的key比較,找到目標(biāo)節(jié)點(diǎn)應(yīng)該插入的位置 if (n != null) { // v=value,存儲節(jié)點(diǎn)value值 // c=compare,存儲兩個節(jié)點(diǎn)比較的大小 Object v; int c; // n的下一個數(shù)據(jù)節(jié)點(diǎn),也就是b的下一個節(jié)點(diǎn)的下一個節(jié)點(diǎn)(孫子節(jié)點(diǎn)) Node f = n.next; // 如果n不為b的下一個節(jié)點(diǎn) // 說明有其它線程修改了數(shù)據(jù),則跳出內(nèi)層循環(huán) // 也就是回到了外層循環(huán)自旋的位置,從頭來過 if (n != b.next) // inconsistent read break; // 如果n的value值為空,說明該節(jié)點(diǎn)已刪除,協(xié)助刪除節(jié)點(diǎn) if ((v = n.value) == null) { // n is deleted // todo 這里為啥會協(xié)助刪除?后面講 n.helpDelete(b, f); break; } // 如果b的值為空或者v等于n,說明b已被刪除 // 這時(shí)候n就是marker節(jié)點(diǎn),那b就是被刪除的那個 if (b.value == null || v == n) // b is deleted break; // 如果目標(biāo)key與下一個節(jié)點(diǎn)的key大 // 說明目標(biāo)元素所在的位置還在下一個節(jié)點(diǎn)的后面 if ((c = cpr(cmp, key, n.key)) > 0) { // 就把當(dāng)前節(jié)點(diǎn)往后移一位 // 同樣的下一個節(jié)點(diǎn)也往后移一位 // 再重新檢查新n是否為空,它與目標(biāo)key的關(guān)系 b = n; n = f; continue; } // 如果比較時(shí)發(fā)現(xiàn)下一個節(jié)點(diǎn)的key與目標(biāo)key相同 // 說明鏈表中本身就存在目標(biāo)節(jié)點(diǎn) if (c == 0) { // 則用新值替換舊值,并返回舊值(onlyIfAbsent=false) if (onlyIfAbsent || n.casValue(v, value)) { @SuppressWarnings("unchecked") V vv = (V)v; return vv; } // 如果替換舊值時(shí)失敗,說明其它線程先一步修改了值,從頭來過 break; // restart if lost race to replace value } // 如果c<0,就往下走,也就是找到了目標(biāo)節(jié)點(diǎn)的位置 // else c < 0; fall through } // 有兩種情況會到這里 // 一是到鏈表尾部了,也就是n為null了 // 二是找到了目標(biāo)節(jié)點(diǎn)的位置,也就是上面的c<0 // 新建目標(biāo)節(jié)點(diǎn),并賦值給z // 這里把n作為新節(jié)點(diǎn)的next // 如果到鏈表尾部了,n為null,這毫無疑問 // 如果c<0,則n的key比目標(biāo)key大,相妝于在b和n之間插入目標(biāo)節(jié)點(diǎn)z z = new Node (key, value, n); // 原子更新b的下一個節(jié)點(diǎn)為目標(biāo)節(jié)點(diǎn)z if (!b.casNext(n, z)) // 如果更新失敗,說明其它線程先一步修改了值,從頭來過 break; // restart if lost race to append to b // 如果更新成功,跳出自旋狀態(tài) break outer; } } // 經(jīng)過Part I,目標(biāo)節(jié)點(diǎn)已經(jīng)插入到有序鏈表中了 // Part II:隨機(jī)決定是否需要建立索引及其層次,如果需要則建立自上而下的索引 // 取個隨機(jī)數(shù) int rnd = ThreadLocalRandom.nextSecondarySeed(); // 0x80000001展開為二進(jìn)制為10000000000000000000000000000001 // 只有兩頭是1 // 這里(rnd & 0x80000001) == 0 // 相當(dāng)于排除了負(fù)數(shù)(負(fù)數(shù)最高位是1),排除了奇數(shù)(奇數(shù)最低位是1) // 只有最高位最低位都不為1的數(shù)跟0x80000001做&操作才會為0 // 也就是正偶數(shù) if ((rnd & 0x80000001) == 0) { // test highest and lowest bits // 默認(rèn)level為1,也就是只要到這里了就會至少建立一層索引 int level = 1, max; // 隨機(jī)數(shù)從最低位的第二位開始,有幾個連續(xù)的1則level就加幾 // 因?yàn)樽畹臀豢隙ㄊ?,正偶數(shù)嘛 // 比如,1100110,level就加2 while (((rnd >>>= 1) & 1) != 0) ++level; // 用于記錄目標(biāo)節(jié)點(diǎn)建立的最高的那層索引節(jié)點(diǎn) Index idx = null; // 取頭索引節(jié)點(diǎn)(這是最高層的頭索引節(jié)點(diǎn)) HeadIndex h = head; // 如果生成的層數(shù)小于等于當(dāng)前最高層的層級 // 也就是跳表的高度不會超過現(xiàn)有高度 if (level <= (max = h.level)) { // 從第一層開始建立一條豎直的索引鏈表 // 這條鏈表使用down指針連接起來 // 每個索引節(jié)點(diǎn)里面都存儲著目標(biāo)節(jié)點(diǎn)這個數(shù)據(jù)節(jié)點(diǎn) // 最后idx存儲的是這條索引鏈表的最高層節(jié)點(diǎn) for (int i = 1; i <= level; ++i) idx = new Index (z, idx, null); } else { // try to grow by one level // 如果新的層數(shù)超過了現(xiàn)有跳表的高度 // 則最多只增加一層 // 比如現(xiàn)在只有一層索引,那下一次最多增加到兩層索引,增加多了也沒有意義 level = max + 1; // hold in array and later pick the one to use // idxs用于存儲目標(biāo)節(jié)點(diǎn)建立的豎起索引的所有索引節(jié)點(diǎn) // 其實(shí)這里直接使用idx這個最高節(jié)點(diǎn)也是可以完成的 // 只是用一個數(shù)組存儲所有節(jié)點(diǎn)要方便一些 // 注意,這里數(shù)組0號位是沒有使用的 @SuppressWarnings("unchecked")Index [] idxs = (Index [])new Index,?>[level+1]; // 從第一層開始建立一條豎的索引鏈表(跟上面一樣,只是這里順便把索引節(jié)點(diǎn)放到數(shù)組里面了) for (int i = 1; i <= level; ++i) idxs[i] = idx = new Index (z, idx, null); // 自旋 for (;;) { // 舊的最高層頭索引節(jié)點(diǎn) h = head; // 舊的最高層級 int oldLevel = h.level; // 再次檢查,如果舊的最高層級已經(jīng)不比新層級矮了 // 說明有其它線程先一步修改了值,從頭來過 if (level <= oldLevel) // lost race to add level break; // 新的最高層頭索引節(jié)點(diǎn) HeadIndex newh = h; // 頭節(jié)點(diǎn)指向的數(shù)據(jù)節(jié)點(diǎn) Node oldbase = h.node; // 超出的部分建立新的頭索引節(jié)點(diǎn) for (int j = oldLevel+1; j <= level; ++j) newh = new HeadIndex (oldbase, newh, idxs[j], j); // 原子更新頭索引節(jié)點(diǎn) if (casHead(h, newh)) { // h指向新的最高層頭索引節(jié)點(diǎn) h = newh; // 把level賦值為舊的最高層級的 // idx指向的不是最高的索引節(jié)點(diǎn)了 // 而是與舊最高層平齊的索引節(jié)點(diǎn) idx = idxs[level = oldLevel]; break; } } } // 經(jīng)過上面的步驟,有兩種情況 // 一是沒有超出高度,新建一條目標(biāo)節(jié)點(diǎn)的索引節(jié)點(diǎn)鏈 // 二是超出了高度,新建一條目標(biāo)節(jié)點(diǎn)的索引節(jié)點(diǎn)鏈,同時(shí)最高層頭索引節(jié)點(diǎn)同樣往上長 // Part III:將新建的索引節(jié)點(diǎn)(包含頭索引節(jié)點(diǎn))與其它索引節(jié)點(diǎn)通過右指針連接在一起 // 這時(shí)level是等于舊的最高層級的,自旋 splice: for (int insertionLevel = level;;) { // h為最高頭索引節(jié)點(diǎn) int j = h.level; // 從頭索引節(jié)點(diǎn)開始遍歷 // 為了方便,這里叫q為當(dāng)前節(jié)點(diǎn),r為右節(jié)點(diǎn),d為下節(jié)點(diǎn),t為目標(biāo)節(jié)點(diǎn)相應(yīng)層級的索引 for (Index q = h, r = q.right, t = idx;;) { // 如果遍歷到了最右邊,或者最下邊, // 也就是遍歷到頭了,則退出外層循環(huán) if (q == null || t == null) break splice; // 如果右節(jié)點(diǎn)不為空 if (r != null) { // n是右節(jié)點(diǎn)的數(shù)據(jù)節(jié)點(diǎn),為了方便,這里直接叫右節(jié)點(diǎn)的值 Node n = r.node; // 比較目標(biāo)key與右節(jié)點(diǎn)的值 int c = cpr(cmp, key, n.key); // 如果右節(jié)點(diǎn)的值為空了,則表示此節(jié)點(diǎn)已刪除 if (n.value == null) { // 則把右節(jié)點(diǎn)刪除 if (!q.unlink(r)) // 如果刪除失敗,說明有其它線程先一步修改了,從頭來過 break; // 刪除成功后重新取右節(jié)點(diǎn) r = q.right; continue; } // 如果比較c>0,表示目標(biāo)節(jié)點(diǎn)還要往右 if (c > 0) { // 則把當(dāng)前節(jié)點(diǎn)和右節(jié)點(diǎn)分別右移 q = r; r = r.right; continue; } } // 到這里說明已經(jīng)到當(dāng)前層級的最右邊了 // 這里實(shí)際是會先走第二個if // 第一個if // j與insertionLevel相等了 // 實(shí)際是先走的第二個if,j自減后應(yīng)該與insertionLevel相等 if (j == insertionLevel) { // 這里是真正連右指針的地方 if (!q.link(r, t)) // 連接失敗,從頭來過 break; // restart // t節(jié)點(diǎn)的值為空,可能是其它線程刪除了這個元素 if (t.node.value == null) { // 這里會去協(xié)助刪除元素 findNode(key); break splice; } // 當(dāng)前層級右指針連接完畢,向下移一層繼續(xù)連接 // 如果移到了最下面一層,則說明都連接完成了,退出外層循環(huán) if (--insertionLevel == 0) break splice; } // 第二個if // j先自減1,再與兩個level比較 // j、insertionLevel和t(idx)三者是對應(yīng)的,都是還未把右指針連好的那個層級 if (--j >= insertionLevel && j < level) // t往下移 t = t.down; // 當(dāng)前層級到最右邊了 // 那只能往下一層級去走了 // 當(dāng)前節(jié)點(diǎn)下移 // 再取相應(yīng)的右節(jié)點(diǎn) q = q.down; r = q.right; } } } return null; } // 尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn) private Node findPredecessor(Object key, Comparator super K> cmp) { // key不能為空 if (key == null) throw new NullPointerException(); // don"t postpone errors // 自旋 for (;;) { // 從最高層頭索引節(jié)點(diǎn)開始查找,先向右,再向下 // 直到找到目標(biāo)位置之前的那個索引 for (Index q = head, r = q.right, d;;) { // 如果右節(jié)點(diǎn)不為空 if (r != null) { // 右節(jié)點(diǎn)對應(yīng)的數(shù)據(jù)節(jié)點(diǎn),為了方便,我們叫右節(jié)點(diǎn)的值 Node n = r.node; K k = n.key; // 如果右節(jié)點(diǎn)的value為空 // 說明其它線程把這個節(jié)點(diǎn)標(biāo)記為刪除了 // 則協(xié)助刪除 if (n.value == null) { if (!q.unlink(r)) // 如果刪除失敗 // 說明其它線程先刪除了,從頭來過 break; // restart // 刪除之后重新讀取右節(jié)點(diǎn) r = q.right; // reread r continue; } // 如果目標(biāo)key比右節(jié)點(diǎn)還大,繼續(xù)向右尋找 if (cpr(cmp, key, k) > 0) { // 往右移 q = r; // 重新取右節(jié)點(diǎn) r = r.right; continue; } // 如果c<0,說明不能再往右了 } // 到這里說明當(dāng)前層級已經(jīng)到最右了 // 兩種情況:一是r==null,二是c<0 // 再從下一級開始找 // 如果沒有下一級了,就返回這個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn) if ((d = q.down) == null) return q.node; // 往下移 q = d; // 重新取右節(jié)點(diǎn) r = d.right; } } } // Node.class中的方法,協(xié)助刪除元素 void helpDelete(Node b, Node f) { /* * Rechecking links and then doing only one of the * help-out stages per call tends to minimize CAS * interference among helping threads. */ // 這里的調(diào)用者this==n,三者關(guān)系是b->n->f if (f == next && this == b.next) { // 將n的值設(shè)置為null后,會先把n的下個節(jié)點(diǎn)設(shè)置為marker節(jié)點(diǎn) // 這個marker節(jié)點(diǎn)的值是它自己 // 這里如果不是它自己說明marker失敗了,重新marker if (f == null || f.value != f) // not already marked casNext(f, new Node (f)); else // marker過了,就把b的下個節(jié)點(diǎn)指向marker的下個節(jié)點(diǎn) b.casNext(this, f.next); } } // Index.class中的方法,刪除succ節(jié)點(diǎn) final boolean unlink(Index succ) { // 原子更新當(dāng)前節(jié)點(diǎn)指向下一個節(jié)點(diǎn)的下一個節(jié)點(diǎn) // 也就是刪除下一個節(jié)點(diǎn) return node.value != null && casRight(succ, succ.right); } // Index.class中的方法,在當(dāng)前節(jié)點(diǎn)與succ之間插入newSucc節(jié)點(diǎn) final boolean link(Index succ, Index newSucc) { // 在當(dāng)前節(jié)點(diǎn)與下一個節(jié)點(diǎn)中間插入一個節(jié)點(diǎn) Node n = node; // 新節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn) newSucc.right = succ; // 原子更新當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)指向新節(jié)點(diǎn) return n.value != null && casRight(succ, newSucc); }
我們這里把整個插入過程分成三個部分:
Part I:找到目標(biāo)節(jié)點(diǎn)的位置并插入
這里的目標(biāo)節(jié)點(diǎn)是數(shù)據(jù)節(jié)點(diǎn),也就是最底層的那條鏈;
尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn)(數(shù)據(jù)節(jié)點(diǎn)都是在最底層的鏈表上);
從這個數(shù)據(jù)節(jié)點(diǎn)開始往后遍歷,直到找到目標(biāo)節(jié)點(diǎn)應(yīng)該插入的位置;
如果這個位置有元素,就更新其值(onlyIfAbsent=false);
如果這個位置沒有元素,就把目標(biāo)節(jié)點(diǎn)插入;
至此,目標(biāo)節(jié)點(diǎn)已經(jīng)插入到最底層的數(shù)據(jù)節(jié)點(diǎn)鏈表中了;
Part II:隨機(jī)決定是否需要建立索引及其層次,如果需要則建立自上而下的索引
取個隨機(jī)數(shù)rnd,計(jì)算(rnd & 0x80000001);
如果不等于0,結(jié)束插入過程,也就是不需要創(chuàng)建索引,返回;
如果等于0,才進(jìn)入創(chuàng)建索引的過程(只要正偶數(shù)才會等于0);
計(jì)算while (((rnd >>>= 1) & 1) != 0),決定層級數(shù),level從1開始;
如果算出來的層級不高于現(xiàn)有最高層級,則直接建立一條豎直的索引鏈表(只有down有值),并結(jié)束Part II;
如果算出來的層級高于現(xiàn)有最高層級,則新的層級只能比現(xiàn)有最高層級多1;
同樣建立一條豎直的索引鏈表(只有down有值);
將頭索引也向上增加到相應(yīng)的高度,結(jié)束Part II;
也就是說,如果層級不超過現(xiàn)有高度,只建立一條索引鏈,否則還要額外增加頭索引鏈的高度(腦補(bǔ)一下,后面舉例說明);
Part III:將新建的索引節(jié)點(diǎn)(包含頭索引節(jié)點(diǎn))與其它索引節(jié)點(diǎn)通過右指針連接在一起(補(bǔ)上right指針)
從最高層級的頭索引節(jié)點(diǎn)開始,向右遍歷,找到目標(biāo)索引節(jié)點(diǎn)的位置;
如果當(dāng)前層有目標(biāo)索引,則把目標(biāo)索引插入到這個位置,并把目標(biāo)索引前一個索引向下移一個層級;
如果當(dāng)前層沒有目標(biāo)索引,則把目標(biāo)索引位置前一個索引向下移一個層級;
同樣地,再向右遍歷,尋找新的層級中目標(biāo)索引的位置,回到第(2)步;
依次循環(huán)找到所有層級目標(biāo)索引的位置并把它們插入到橫向的索引鏈表中;
總結(jié)起來,一共就是三大步:
插入目標(biāo)節(jié)點(diǎn)到數(shù)據(jù)節(jié)點(diǎn)鏈表中;
建立豎直的down鏈表;
建立橫向的right鏈表;
添加元素舉例假設(shè)初始鏈表是這樣:
假如,我們現(xiàn)在要插入一個元素9。
(1)尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn),在這里也就是找到了5這個數(shù)據(jù)節(jié)點(diǎn);
(2)從5開始向后遍歷,找到目標(biāo)節(jié)點(diǎn)的位置,也就是在8和12之間;
(3)插入9這個元素,Part I 結(jié)束;
然后,計(jì)算其索引層級,假如是3,也就是level=3。
(1)建立豎直的down索引鏈表;
(2)超過了現(xiàn)有高度2,還要再增加head索引鏈的高度;
(3)至此,Part II 結(jié)束;
最后,把right指針補(bǔ)齊。
(1)從第3層的head往右找當(dāng)前層級目標(biāo)索引的位置;
(2)找到就把目標(biāo)索引和它前面索引的right指針連上,這里前一個正好是head;
(3)然后前一個索引向下移,這里就是head下移;
(4)再往右找目標(biāo)索引的位置;
(5)找到了就把right指針連上,這里前一個是3的索引;
(6)然后3的索引下移;
(7)再往右找目標(biāo)索引的位置;
(8)找到了就把right指針連上,這里前一個是5的索引;
(9)然后5下移,到底了,Part III 結(jié)束,整個插入過程結(jié)束;
刪除元素刪除元素,就是把各層級中對應(yīng)的元素刪除即可.
public V remove(Object key) { return doRemove(key, null); } final V doRemove(Object key, Object value) { // key不為空 if (key == null) throw new NullPointerException(); Comparator super K> cmp = comparator; // 自旋 outer: for (;;) { // 尋找目標(biāo)節(jié)點(diǎn)之前的最近的索引節(jié)點(diǎn)對應(yīng)的數(shù)據(jù)節(jié)點(diǎn) // 為了方便,這里叫b為當(dāng)前節(jié)點(diǎn),n為下一個節(jié)點(diǎn),f為下下個節(jié)點(diǎn) for (Nodeb = findPredecessor(key, cmp), n = b.next;;) { Object v; int c; // 整個鏈表都遍歷完了也沒找到目標(biāo)節(jié)點(diǎn),退出外層循環(huán) if (n == null) break outer; // 下下個節(jié)點(diǎn) Node f = n.next; // 再次檢查 // 如果n不是b的下一個節(jié)點(diǎn)了 // 說明有其它線程先一步修改了,從頭來過 if (n != b.next) // inconsistent read break; // 如果下個節(jié)點(diǎn)的值奕為null了 // 說明有其它線程標(biāo)記該元素為刪除狀態(tài)了 if ((v = n.value) == null) { // n is deleted // 協(xié)助刪除 n.helpDelete(b, f); break; } // 如果b的值為空或者v等于n,說明b已被刪除 // 這時(shí)候n就是marker節(jié)點(diǎn),那b就是被刪除的那個 if (b.value == null || v == n) // b is deleted break; // 如果c<0,說明沒找到元素,退出外層循環(huán) if ((c = cpr(cmp, key, n.key)) < 0) break outer; // 如果c>0,說明還沒找到,繼續(xù)向右找 if (c > 0) { // 當(dāng)前節(jié)點(diǎn)往后移 b = n; // 下一個節(jié)點(diǎn)往后移 n = f; continue; } // c=0,說明n就是要找的元素 // 如果value不為空且不等于找到元素的value,不需要刪除,退出外層循環(huán) if (value != null && !value.equals(v)) break outer; // 如果value為空,或者相等 // 原子標(biāo)記n的value值為空 if (!n.casValue(v, null)) // 如果刪除失敗,說明其它線程先一步修改了,從頭來過 break; // P.S.到了這里n的值肯定是設(shè)置成null了 // 關(guān)鍵!?。?! // 讓n的下一個節(jié)點(diǎn)指向一個market節(jié)點(diǎn) // 這個market節(jié)點(diǎn)的key為null,value為marker自己,next為n的下個節(jié)點(diǎn)f // 或者讓b的下一個節(jié)點(diǎn)指向下下個節(jié)點(diǎn) // 注意:這里是或者||,因?yàn)閮蓚€CAS不能保證都成功,只能一個一個去嘗試 // 這里有兩層意思: // 一是如果標(biāo)記market成功,再嘗試將b的下個節(jié)點(diǎn)指向下下個節(jié)點(diǎn),如果第二步失敗了,進(jìn)入條件,如果成功了就不用進(jìn)入條件了 // 二是如果標(biāo)記market失敗了,直接進(jìn)入條件 if (!n.appendMarker(f) || !b.casNext(n, f)) // 通過findNode()重試刪除(里面有個helpDelete()方法) findNode(key); // retry via findNode else { // 上面兩步操作都成功了,才會進(jìn)入這里,不太好理解,上面兩個條件都有非"!"操作 // 說明節(jié)點(diǎn)已經(jīng)刪除了,通過findPredecessor()方法刪除索引節(jié)點(diǎn) // findPredecessor()里面有unlink()操作 findPredecessor(key, cmp); // clean index // 如果最高層頭索引節(jié)點(diǎn)沒有右節(jié)點(diǎn),則跳表的高度降級 if (head.right == null) tryReduceLevel(); } // 返回刪除的元素值 @SuppressWarnings("unchecked") V vv = (V)v; return vv; } } return null; }
(1)尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn)(數(shù)據(jù)節(jié)點(diǎn)都是在最底層的鏈表上);
(2)從這個數(shù)據(jù)節(jié)點(diǎn)開始往后遍歷,直到找到目標(biāo)節(jié)點(diǎn)的位置;
(3)如果這個位置沒有元素,直接返回null,表示沒有要刪除的元素;
(4)如果這個位置有元素,先通過n.casValue(v, null)原子更新把其value設(shè)置為null;
(5)通過n.appendMarker(f)在當(dāng)前元素后面添加一個marker元素標(biāo)記當(dāng)前元素是要刪除的元素;
(6)通過b.casNext(n, f)嘗試刪除元素;
(7)如果上面兩步中的任意一步失敗了都通過findNode(key)中的n.helpDelete(b, f)再去不斷嘗試刪除;
(8)如果上面兩步都成功了,再通過findPredecessor(key, cmp)中的q.unlink(r)刪除索引節(jié)點(diǎn);
(9)如果head的right指針指向了null,則跳表高度降級;
刪除元素舉例假如初始跳表如下圖所示,我們要刪除9這個元素。
(1)找到9這個數(shù)據(jù)節(jié)點(diǎn);
(2)把9這個節(jié)點(diǎn)的value值設(shè)置為null;
(3)在9后面添加一個marker節(jié)點(diǎn),標(biāo)記9已經(jīng)刪除了;
(4)讓8指向12;
(5)把索引節(jié)點(diǎn)與它前一個索引的right斷開聯(lián)系;
(6)跳表高度降級;
至于,為什么要有(2)(3)(4)這么多步驟呢,因?yàn)槎嗑€程下如果直接讓8指向12,可以其它線程先一步在9和12間插入了一個元素10呢,這時(shí)候就不對了。
所以這里搞了三步來保證多線程下操作的正確性。
如果第(2)步失敗了,則直接重試;
如果第(3)或(4)步失敗了,因?yàn)榈冢?)步是成功的,則通過helpDelete()不斷重試去刪除;
其實(shí)helpDelete()里面也是不斷地重試(3)和(4);
只有這三步都正確完成了,才能說明這個元素徹底被刪除了。
查找元素經(jīng)過上面的插入和刪除,查找元素就比較簡單了:
public V get(Object key) { return doGet(key); } private V doGet(Object key) { // key不為空 if (key == null) throw new NullPointerException(); Comparator super K> cmp = comparator; // 自旋 outer: for (;;) { // 尋找目標(biāo)節(jié)點(diǎn)之前最近的索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn) // 為了方便,這里叫b為當(dāng)前節(jié)點(diǎn),n為下個節(jié)點(diǎn),f為下下個節(jié)點(diǎn) for (Nodeb = findPredecessor(key, cmp), n = b.next;;) { Object v; int c; // 如果鏈表到頭還沒找到元素,則跳出外層循環(huán) if (n == null) break outer; // 下下個節(jié)點(diǎn) Node f = n.next; // 如果不一致讀,從頭來過 if (n != b.next) // inconsistent read break; // 如果n的值為空,說明節(jié)點(diǎn)已被其它線程標(biāo)記為刪除 if ((v = n.value) == null) { // n is deleted // 協(xié)助刪除,再重試 n.helpDelete(b, f); break; } // 如果b的值為空或者v等于n,說明b已被刪除 // 這時(shí)候n就是marker節(jié)點(diǎn),那b就是被刪除的那個 if (b.value == null || v == n) // b is deleted break; // 如果c==0,說明找到了元素,就返回元素值 if ((c = cpr(cmp, key, n.key)) == 0) { @SuppressWarnings("unchecked") V vv = (V)v; return vv; } // 如果c<0,說明沒找到元素 if (c < 0) break outer; // 如果c>0,說明還沒找到,繼續(xù)尋找 // 當(dāng)前節(jié)點(diǎn)往后移 b = n; // 下一個節(jié)點(diǎn)往后移 n = f; } } return null; }
(1)尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn)(數(shù)據(jù)節(jié)點(diǎn)都是在最底層的鏈表上);
(2)從這個數(shù)據(jù)節(jié)點(diǎn)開始往后遍歷,直到找到目標(biāo)節(jié)點(diǎn)的位置;
(3)如果這個位置沒有元素,直接返回null,表示沒有找到元素;
(4)如果這個位置有元素,返回元素的value值;
查找元素舉例假如有如下圖所示這個跳表,我們要查找9這個元素,它走過的路徑是怎樣的呢?可能跟你相像的不一樣。。
(1)尋找目標(biāo)節(jié)點(diǎn)之前最近的一個索引對應(yīng)的數(shù)據(jù)節(jié)點(diǎn),這里就是5;
(2)從5開始往后遍歷,經(jīng)過8,到9;
(3)找到了返回;
整個路徑如下圖所示:
為啥不從9的索引直接過來呢?實(shí)際打斷點(diǎn)調(diào)試來看確實(shí)是按照上圖的路徑來走的。
我猜測可能是因?yàn)閒indPredecessor()這個方法是插入、刪除、查找元素多個方法共用的,在單鏈表中插入和刪除元素是需要記錄前一個元素的,而查找并不需要,這里為了兼容三者使得編碼相對簡單一點(diǎn),所以就使用了同樣的邏輯,而沒有多帶帶對查找元素進(jìn)行優(yōu)化。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76268.html
摘要:介紹底層是通過來實(shí)現(xiàn)的,它是一個有序的線程安全的集合。源碼分析它的源碼比較簡單,跟通過實(shí)現(xiàn)的基本是一致,只是多了一些取最近的元素的方法。 介紹 ConcurrentSkipListSet底層是通過ConcurrentNavigableMap來實(shí)現(xiàn)的,它是一個有序的線程安全的集合。 源碼分析 它的源碼比較簡單,跟通過Map實(shí)現(xiàn)的Set基本是一致,只是多了一些取最近的元素的方法。 // ...
摘要:同步容器及其注意事項(xiàng)中的容器主要可以分為四個大類,分別是和,但并不是所有的容器都是線程安全的。并發(fā)容器及其注意事項(xiàng)在版本之前所謂的線程安全的容器,主要指的就是同步容器,當(dāng)然因?yàn)樗蟹椒ǘ加脕肀WC互斥,串行度太高了,性能太差了。 Java 并發(fā)包有很大一部分內(nèi)容都是關(guān)于并發(fā)容器的,因此學(xué)習(xí)和搞懂這部分的內(nèi)容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
摘要:源碼分析屬性內(nèi)部使用虛擬對象,用來作為放到中構(gòu)造方法非,主要是給使用的構(gòu)造方法都是調(diào)用對應(yīng)的構(gòu)造方法。遍歷元素直接調(diào)用的的迭代器。什么是機(jī)制是集合中的一種錯誤機(jī)制。當(dāng)使用迭代器迭代時(shí),如果發(fā)現(xiàn)集合有修改,則快速失敗做出響應(yīng),拋出異常。 簡介 集合,這個概念有點(diǎn)模糊。 廣義上來講,java中的集合是指java.util包下面的容器類,包括和Collection及Map相關(guān)的所有類。 中...
摘要:源碼解析屬性雙向鏈表頭節(jié)點(diǎn)雙向鏈表尾節(jié)點(diǎn)是否按訪問順序排序雙向鏈表的頭節(jié)點(diǎn),舊數(shù)據(jù)存在頭節(jié)點(diǎn)。雙向鏈表的尾節(jié)點(diǎn),新數(shù)據(jù)存在尾節(jié)點(diǎn)。內(nèi)部類位于中位于中存儲節(jié)點(diǎn),繼承自的類,用于單鏈表存儲于桶中,和用于雙向鏈表存儲所有元素。 簡介 LinkedHashMap內(nèi)部維護(hù)了一個雙向鏈表,能保證元素按插入的順序訪問,也能以訪問順序訪問,可以用來實(shí)現(xiàn)LRU緩存策略。 LinkedHashMap可以看...
閱讀 3424·2021-11-24 09:38
閱讀 3201·2021-11-22 09:34
閱讀 2113·2021-09-22 16:03
閱讀 2378·2019-08-29 18:37
閱讀 383·2019-08-29 16:15
閱讀 1776·2019-08-26 13:56
閱讀 872·2019-08-26 12:21
閱讀 2211·2019-08-26 12:15