看了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);
}
复制代码
去除索引条件
为啥要这么多判断啊,为啥要回退啊,我是彻底不懂,只能说确定是为了处理线程同步问题的,哪位读者知道,谢谢留言告诉我
技术变化都很快,但基础技术、理论知识永远都是那些;做者但愿在余后的生活中,对经常使用技术点进行基础知识分享;若是你以为文章写的不错,请给与关注和点赞;若是文章存在错误,也请多多指教!