什么是跳表
跳表(Skip List)是一种类似于链表的数据结构,其查询、插入、删除的时间复杂度都是O(logn)。
在传统的单链表结构中,查找某个元素需要从链表的头部按顺序遍历,直到找到目标元素为止,查找的时间复杂度为O(n)。
而跳表结合了树和跳表的特点,其特性如下:
- 跳表由很多层组成;
- 每一层都是一个有序的链表;
- 最底层的链表包含所有元素;
- 对于每一层的任意一个节点,不仅有指向其下一个节点的指针,也有指向其下一层的指针;
- 如果一个元素出现Level n层的链表中,则它在Level n层以下的链表也都会出现。
核心思想
- 第一层(最底层)上有N个节点,即所有节点都存在于第一层上;
- 高度随机决定,扔硬币问题,0.5的概率为正面,0.5的概率为反面,抛出正面则层数加一,即高度加一;
- 第一层有N个节点,第二层以0.5的概率扔硬币,所以第二层有N/2个,同理第三层有N/4,一次类推,所以当有N个节点的时候,形成跳表结构,需要logN次,跟输入规律没关系,跟概率有关系,但是概率问题也能logN。
- 从最高层一次往下找,高链表上跨一个节点,底下不知道跨了多少个节点,这就是跳表的好处。
跳表实际上是一种空间换时间的数据结构
ConcurrentSkipListMap:java中的跳表
ConcurrentSkipListMap是在JDK1.6中新增的,为了对高并发场景下的有序Map提供很好的支持,它有几个特点:
- 高并发场景
- key是有序的
- 添加、删除、查找操作都是基于跳表结构(Skip List)实现的
- key和value都不能为null
ConcurrentSkipListMap结构
- Node节点代表了真正存储数据的节点,包含了key、value、指向下一个节点的指针next:
static final class Node<K,V> {
final K key;
volatile Object value;
volatile Node<K,V> next;
/**
* Creates a new regular node.
*/
Node(K key, Object value, Node<K,V> next) {
this.key = key;
this.value = value;
this.next = next;
}
}
- Index节点代表了跳表的层级,包含了当前节点node、下一层down、当前层的下一个节点right
static class Index<K,V> {
final Node<K,V> node;
final Index<K,V> down;
volatile Index<K,V> right;
/**
* Creates index node with given values.
*/
Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
this.node = node;
this.down = down;
this.right = right;
}
}
如图所示,Node节点将真实的数据按顺序链接起来,Index节点组成了条表中多层次的索引结构
## 案例
public class ConcurrentSkipListMapDemo {
public static void main(String[] args) {
ConcurrentSkipListMap<Integer, String> skipListMap = new ConcurrentSkipListMap<>();
skipListMap.put(4, "4");
skipListMap.put(5, "5");
skipListMap.put(1, "1");
skipListMap.put(6, "6");
skipListMap.put(2, "2");
System.out.println(skipListMap.keySet());
System.out.println(skipListMap);
System.out.println(skipListMap.descendingKeySet());
System.out.println(skipListMap.descendingMap());
}
}
运行结果: