看了TreeMap、HashMap源碼的,或者手寫實現紅黑樹、平衡二叉樹的,感受插入和刪除手寫太難了;我邏輯理解也不是很順暢,插入基本搞清楚了,可是刪除,我就呵呵了node
那麼,有沒有一種性能和紅黑樹或者平衡二叉樹不相上下,且又很好實現的數據結構,答案是確定,我知道的至少有一種 跳錶;不過也有缺點,看實現方式(我找的資料都是插入時,對剛插入的數據隨機處理索引),不必定穩定安全
什麼叫跳錶呢? 跳錶是一個隨機化的有序鏈表。跳錶在原有的有序鏈表上面增長了多級索引,經過索引來實現快速查找。跳錶不只能提升搜索性能,同時也能夠提升插入和刪除操做的性能。bash
它採用隨機技術決定鏈表中哪些節點應增長向前指針以及在該節點中應增長多少個指針。 採用這種隨機技術,跳錶中的搜索、插入、刪除操做的時間均爲O(logn),然而,最壞狀況下時間複雜性卻變成O(n)。 跳錶的結構大體以下圖:(來源百度百科)數據結構
final K key;
volatile Object value;
volatile Node<K,V> next;
複製代碼
節點數據包含,key,value和下個數據;也就是採用單鏈表的結構存儲實際數據app
具體的代碼操做就不貼出了,大概有如下內容:dom
final Node<K,V> node;
final Index<K,V> down;
volatile Index<K,V> right;
複製代碼
包含對應數據,下方索引節點,右邊索引節點;源碼分析
static final class HeadIndex<K,V> extends Index<K,V> {
final int level;
HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
super(node, down, right);
this.level = level;
}
}
複製代碼
繼承了普通索引節點,增長了索引級別,從1開始;post
static final Object BASE_HEADER = new Object();
private transient volatile HeadIndex<K,V> head;
final Comparator<? super K> comparator;
複製代碼
head 索引節點,comparator比較器;特色性能
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException(); // don't postpone errors for (;;) { for (Index<K,V> q = head, r = q.right, d;;) { if (r != null) { Node<K,V> n = r.node; K k = n.key; if (n.value == null) { if (!q.unlink(r)) break; r = q.right; continue; } if (cpr(cmp, key, k) > 0) { q = r; r = r.right; continue; } } if ((d = q.down) == null) return q.node; q = d; r = d.right; } } } 複製代碼
向下移動,則是把down指針域當成next,進行鏈表移動ui
向右移動,則是把right指針域當成next,進行鏈表移動
private Node<K,V> findNode(Object key) {
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
outer: for (;;) {
for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
Object v; int c;
if (n == null)
break outer;
Node<K,V> f = n.next;
if (n != b.next)
break;
if ((v = n.value) == null) {
n.helpDelete(b, f);
break;
}
if (b.value == null || v == n)
break;
if ((c = cpr(cmp, key, n.key)) == 0)
return n;
if (c < 0)
break outer;
b = n;
n = f;
}
}
return null;
}
複製代碼
因而可知,數據節點按照key的從小到大排列的
public V get(Object key) {
return doGet(key);
}
private V doGet(Object key) {
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
outer: for (;;) {
for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
Object v; int c;
if (n == null)
break outer;
Node<K,V> f = n.next;
if (n != b.next)
break;
if ((v = n.value) == null) {
n.helpDelete(b, f);
break;
}
if (b.value == null || v == n)
break;
if ((c = cpr(cmp, key, n.key)) == 0) {
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
if (c < 0)
break outer;
b = n;
n = f;
}
}
return null;
}
複製代碼
和findNode方法基本一致,就是在下面代碼中,返回值不一樣;一個是返回節點,一個是返回節點value值
if ((c = cpr(cmp, key, n.key)) == 0) {
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
複製代碼
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
return doPut(key, value, false);
}
private V doPut(K key, V value, boolean onlyIfAbsent) {
Node<K,V> z;
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
outer: for (;;) {
for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
if (n != null) {
Object v; int c;
Node<K,V> f = n.next;
if (n != b.next)
break;
if ((v = n.value) == null) {
n.helpDelete(b, f);
break;
}
if (b.value == null || v == n)
break;
if ((c = cpr(cmp, key, n.key)) > 0) {
b = n;
n = f;
continue;
}
if (c == 0) {
if (onlyIfAbsent || n.casValue(v, value)) {
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
break; // restart if lost race to replace value
}
}
z = new Node<K,V>(key, value, n);
if (!b.casNext(n, z))
break;
break outer;
}
}
int rnd = ThreadLocalRandom.nextSecondarySeed();
if ((rnd & 0x80000001) == 0) {
int level = 1, max;
while (((rnd >>>= 1) & 1) != 0)
++level;
Index<K,V> idx = null;
HeadIndex<K,V> h = head;
if (level <= (max = h.level)) {
for (int i = 1; i <= level; ++i)
idx = new Index<K,V>(z, idx, null);
}
else {
level = max + 1;
@SuppressWarnings("unchecked")Index<K,V>[] idxs =
(Index<K,V>[])new Index<?,?>[level+1];
for (int i = 1; i <= level; ++i)
idxs[i] = idx = new Index<K,V>(z, idx, null);
for (;;) {
h = head;
int oldLevel = h.level;
if (level <= oldLevel) // lost race to add level
break;
HeadIndex<K,V> newh = h;
Node<K,V> oldbase = h.node;
for (int j = oldLevel+1; j <= level; ++j)
newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
if (casHead(h, newh)) {
h = newh;
idx = idxs[level = oldLevel];
break;
}
}
}
splice: for (int insertionLevel = level;;) {
int j = h.level;
for (Index<K,V> q = h, r = q.right, t = idx;;) {
if (q == null || t == null)
break splice;
if (r != null) {
Node<K,V> n = r.node;
int c = cpr(cmp, key, n.key);
if (n.value == null) {
if (!q.unlink(r))
break;
r = q.right;
continue;
}
if (c > 0) {
q = r;
r = r.right;
continue;
}
}
if (j == insertionLevel) {
if (!q.link(r, t))
break;
if (t.node.value == null) {
findNode(key);
break splice;
}
if (--insertionLevel == 0)
break splice;
}
if (--j >= insertionLevel && j < level)
t = t.down;
q = q.down;
r = q.right;
}
}
}
return null;
}
複製代碼
從上面代碼能夠看出,分兩個大步驟:一是插入數據域節點(修改值只有這一步),二是索引層處理
插入數據:和get思路大同小異,存在一下區別
索引層處理
public V remove(Object key) {
return doRemove(key, null);
}
final V doRemove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
outer: for (;;) {
for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
Object v; int c;
if (n == null)
break outer;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
if (b.value == null || v == n) // b is deleted
break;
if ((c = cpr(cmp, key, n.key)) < 0)
break outer;
if (c > 0) {
b = n;
n = f;
continue;
}
/////////////////////////////////////////////// 相等
if (value != null && !value.equals(v))
break outer;
if (!n.casValue(v, null))
break;
if (!n.appendMarker(f) || !b.casNext(n, f))
findNode(key); // retry via findNode
else {
findPredecessor(key, cmp); // clean index
if (head.right == null)
tryReduceLevel();
}
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
}
return null;
}
複製代碼
又是熟悉的套路;校驗,雙層循環;那咱們從key值比較相等(代碼中 /////////////////////////////////////////////// 相等 處)開始提及
private void tryReduceLevel() {
HeadIndex<K,V> h = head;
HeadIndex<K,V> d;
HeadIndex<K,V> e;
if (h.level > 3 &&
(d = (HeadIndex<K,V>)h.down) != null &&
(e = (HeadIndex<K,V>)d.down) != null &&
e.right == null &&
d.right == null &&
h.right == null &&
casHead(h, d) &&
h.right != null)
casHead(d, h);
}
複製代碼
去除索引條件
爲啥要這麼多判斷啊,爲啥要回退啊,我是徹底不懂,只能說確定是爲了處理線程同步問題的,哪位讀者知道,謝謝留言告訴我
技術變化都很快,但基礎技術、理論知識永遠都是那些;做者但願在餘後的生活中,對經常使用技術點進行基礎知識分享;若是你以爲文章寫的不錯,請給與關注和點贊;若是文章存在錯誤,也請多多指教!