zoukankan      html  css  js  c++  java
  • 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);
    }
    

      

     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    不上大学遗撼,上过大学后悔
    消息队列(MSMQ)实现多服务器应用程序之间消息实时交互
    抓取网页源代码
    用asp.net显示在线登陆人数及位置
    原版对XML文档的读写
    C#.NET实现经典排序算法
    深入剖析C#继承机制
    ASP.NET长文章分页
    人民币小写金额转化成大写金额
    GridView和DataFormatString
  • 原文地址:https://www.cnblogs.com/juncaoit/p/12513413.html
Copyright © 2011-2022 走看看