JUC-跳表-ConcurrentSkipListMap

1.概述

  跳表(跳跃表)是一种数据结构,改进自链表,用于存储有序的数据,跳跃表通过空间换时间的方法来提高数据检索的速度。

  需要排序的东西不需要持久化或者为了更高的效率,是直接在内存中进行排序,那么使用到的数据结构就是jdk中的ConcurrentSkipListMap(支持同步)或者TreeMap(不支持同步,性能更好)

2.场景

  通过上面的介绍,我们了解到跳跃表的应用场景大概是这样的:

  1. 有序
  2. 频繁插入删除
  3. 频繁的查找

  对于有序来说,数组和普通链表都可以通过排序算法来实现,在排序复杂度上不相上下。链表在插入和删除上性能较好,数组在查找上性能较好,所以都不能满足我们的要求。跳跃表则是在插入删除和查找的性能上做了折中,复杂度为log(n)。

  跳表结构如下所示:

 
  为了更好的支持插入和删除,所以采用链表的形式,可以看到图片中最下面一行是一个有序的链表。但如果只是一个单一的链表,查找时复杂度为O(n),性能太差。如何优化呢?

  在有序数组中,我们查找时用的是二分查找,一次查找可以排除一半元素的遍历。在数组中之所以可以用二分查找,是因为我们能够快速的以O(1)的复杂度定位到中间的位置,但是链表只能是O(N)。所以跳跃表采取空间换时间的方式,既然你找不到中间点,或者三分之一点等中间位置,那么我可以通过多增加一个节点来指向中间位置,这样你也能够快速的定位到中间的位置,然后一定程度的减少你遍历元素的个数,提高效率。图中有多层,相邻的两层,采用的都是这样的思想。


3.节点
  通过上图,可以发现有两种节点类型,第一种是最下层的,与普通链表相似,第二种是除了最后一层以外的其他索引层节点,有两个指针。
  但是在jdk源码中还存在一种节点,是索引层的头节点,还维护了其层数信息
 
// Node是最底层的链表的节点,包括键值对和指向下一个节点的指针
static final class Node<K,V> {
    final K key;
    volatile Object value;
    volatile Node<K,V> next;
// 至于为什么需要两个构造函数,后面源码会有解释
    Node(K key, Object value, Node<K,V> next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }
    Node(Node<K,V> next) {
        this.key = null;
        this.value = this;
        this.next = next;
    }
    // ...配套method
}

// 索引节点结构
// 存储了两个指针,分别指向右边和下边的节点
// 索引节点的value为链表节点
static class Index<K,V> {
    final Node<K,V> node;
    final Index<K,V> down;
    volatile Index<K,V> right;

    Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
        this.node = node;
        this.down = down;
        this.right = right;
    }
    // ...配套method
}

// 索引层的头节点结构
// 在索引节点的基础上添加了表示层数的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;
    }
}

  

4.构造函数

// Compartor接口用来指定key的排序规则
public ConcurrentSkipListMap() {
    this.comparator = null;
    initialize();
}
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
    initialize();
}
// 还有两个传入map和sortedMap的构造函数
private void initialize() {
    keySet = null;// 内部类
    entrySet = null;// 内部类
    values = null;// 内部类
    descendingMap = null;// 内部类
// private static final Object BASE_HEADER = new Object();
// 从注释给出的图来看,这个head应该是一直处于第一层的头节点
    head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
                              null, null, 1);
}

  

 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/juncaoit/p/12513413.html