跳表是一种可以用来快速查找的数据结构,类似平衡树,但平衡树的插入和删除很可能导致平衡树的全局调整,而跳表只需要局部调整;在高并发情况下,需为保证线程安全,对于平衡树需要一个全局锁,而对于跳表只需要部分锁即可;跳表的时间复杂度是O(log n),空间复杂度是O(n);
跳表具有如下性质:
(1) 由很多层结构组成
(2) 每一层都是一个有序的链表
(3) 最底层(Level 1)的链表包含所有元素
(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。
ConcurrentSkipListMap特点:
- 有序链表实现;
- 无锁实现;
- value值不能为空;
- 层级越高跳跃性越大,数据越少,查询理论变快;
- 新的Node是否抽出来作为Index,随机决定;
- Index对应的value由随机数决定;
- 每层元素,HeadIndex固定为所有Node中最小的;
- 很多开源组件中有使用,如Level DB、Redis等;
从结构上很明显,跳表是一种使用空间换时间的算法;
跳表内所有链表元素是排序的;查找时,可以从顶级链表开始,一旦发现被查找元素大于当前链表中值,转入下一层链表继续查找,因此搜索是跳跃式的;
ConcurrentSkipListMap类中关键数据结构
1、Node节点,封装Map中的key和value,以及next下一个节点,并且使用CAS方法设置当前value及next节点值;
/**
* Nodes hold keys and values, and are singly linked in sorted
* order, possibly with some intervening marker nodes. The list is
* headed by a dummy node accessible as head.node. The value field
* is declared only as Object because it takes special non-V
* values for marker and header nodes.
*/
static final class Node<K,V> {
final K key;
volatile Object value;
volatile Node<K,V> next;
/**
* compareAndSet value field
*/
boolean casValue(Object cmp, Object val) {
return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
}
/**
* compareAndSet next field
*/
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
...
}
2、Index,封装Node节点,并且保存向下和向右的引用;
/**
* Index nodes represent the levels of the skip list. Note that
* even though both Nodes and Indexes have forward-pointing
* fields, they have different types and are handled in different
* ways, that can't nicely be captured by placing field in a
* shared abstract class.
*/
static class Index<K,V> {
final Node<K,V> node;
final Index<K,V> down;
volatile Index<K,V> right;
...
}
3、HeadIndex,链表头部第一个Index,记录当前表头处于哪一层,当前值以及向下、向右的引用;
/**
* Nodes heading each level keep track of their level.
*/
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;
}
}