定义
- skipList 跳表是由 William Pugh在1990年发表的论文中提出的一种数据结构。其思想是用空间换取时间,在一个基础链表中如果想查找一个元素那么就需要遍历整个链表,最坏情况的时间复杂度O(n)。在mysql数据库中因为有索引的存在,所以查询效率是非常高的,所以就把索引的概念提到链表中,我们可以在一个基础链表中提取出其中某些元素成为一个新的链表,这个新的链表就可以当成索引链表。如果这个索引链表的长度还可以继续提取,那么就在这个索引链表上继续提取某些元素,创建第二列索引列表。基础链表+索引链表就是跳表结构。
- 跳表是一种可以用来代替平衡树的数据结构,跳表使用概率平衡而不是严格平衡,所以与平衡树相比,跳表的插入和删除会简单的很多,且速度也会快很多。
具体实现
- skipList跳表的代码实现相对而言是比较复杂的,而且使用的场景也是比较少,所以这里直接使用在Java jdk里有一个ConcurrentSkipListMap已经实现了跳表的数据结构。
- ConcurrentSkipListMap中主要的三个关键结构:
/**
* 节点拥有键和值,并按排序单独链接顺序,可能带有一些中间的标记节点。
* 清单是以可作为head.node访问的虚拟节点为首。
* 值栏位仅声明为Object,因为它需要特殊的非V标记和标头节点的值。
*
*/
static final class Node<K,V> {
final K key; // 键
volatile Object value; // 值
volatile Node<K,V> next; // 下一个节点
...
}
/**
* 索引节点代表跳过列表的级别。
* 注意即使节点和索引都具有前向指向字段,它们具有不同的类型,并且处理方式不同方式,将字段放在共享的抽象类。
*/
static class Index<K,V> {
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> {
// 相对于index,多了个level,用于表示当前索引链表级别
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;
}
}
它们之间的关系是
另外ConcurrentSkipListMap源码我也看得似懂非懂的,这里就不在继续深入了解了。
redis中使用场景
- 在redis的五种数据结构中,其内部都采用不同且多样的内部编码
数据结构 | 内部编码 |
---|---|
String | int,raw,embstr |
hash | ziplist,hashtable |
list | ziplist,linkedlist |
set | intset,hashtable |
zset | ziplist,skiplist |
- 在redis.config配置文件中有两个配置
- 表示当有序集合元素个数小于等于 128,并且元素长度小于64时,zset使用的是ziplist。反之使用skiplist。