标签:好的 == down ali 跳表 静态 构建 lan tun
跳表(SkipList)是一种可用来进行快速查找的数据结构,时间复杂度为O(logn),有点类似于平衡树。之所以这么说,是因为它们都可以对元素进行快速的查找。但二者有一个重要的区别:对平衡树的修改(也就是所谓的插入和删除)往往很可能会导致平衡树进行一次全局的调整(也就是所谓的调平),而对跳表而言,插入和删除只需要对整个数据结构的局部进行调整即可。这样的好处是显而易见的:
需要调整的规模较小,且数据量越大所带来的性能提升自然也越大。
在并发环境下,当需要修改跳表时,我们不需要将整张表锁起来,而可以只锁住本次修改所影响的区域,提高性能。
跳表的另一个特点就是随机算法。跳表的本质是同时维护多个链表,且这些链表是分层的。如下图所示:
最底层的链表包含跳表的所有元素,每往上一层的链表都是下面链表的子集(又是典型的用空间换时间的策略),一个元素被插入哪些层完全是随机的(当然,最下面那层是必然会被插入的),因此如果运气不好的话,也许会得到一个性能很糟糕的结构。不过在实际使用中,尤其是数据量较大时,概率将会极大的掩盖运气,跳表的性能通常都是非常好的。
跳表内所有链表的元素都是具有偏序关系的(所以才说跳表和平衡树像嘛)。查找时,通常是从顶层链表开始找起,一旦发现元素值大于待搜索值,或者找到末尾也未找到,则说明本层已无找到的可能,便进入下一层,直到找到或是遍历完底层也没找到(此时说明确实没有)。
下图是一个查找值7的小例子:
java API中提供的跳表为java.util.concurrent.ConcurrentSkipListMap,类定义如下:
1 | public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> implements ConcurrentNavigableMap<K,V>, Cloneable, java.io.Serializable |
这是一个Map,它与我们在Java API中最常见的使用哈希算法实现的Map有一个显著的不同:哈希并不会保存元素的顺序,而跳表因其特性其元素自然是有序的(所以说,并非所有Map都是无序的)。因此如果需要一个有序的Map,那么跳表可能是很好的选择。
我们可以先来看一个小例子:
1 | import java.util.Map; |
输出:
1 | 0 |
既然跳表底层是以链表实现的,那么它最重要的数据结构自然就是节点了。对于ConcurrentSkipListMap而言,节点类是他的静态成员内部类。全部代码如下:
1 | static final class Node<K,V> { |
简单看下来,最大的感触自然就是全程没有出现synchronized或Lock,看来又是一个只依赖CAS实现并发控制的线程安全的容器(之所以说又,是因为我想到了Java 并发-ConcurrentLinkedQueue)
除了Node外,ConcurrentSkipListMap中另一个重要的数据结构名为Index。它也是ConcurrentSkipListMap的静态成员内部类,类定义如下:
1 | static class Index<K,V> |
Index负责将各层链表拼接起来,它有如下关键的实例成员变量:
1 | // Index内部封装的节点 |
也就是说,从外部看,ConcurrentSkipListMap中存储的基本元素是Index,它负责进行网络的构建,其内部封装着存储实际业务逻辑的Node。
此外,每一层链表的表头Index中还要存储本行链表是第几层。ConcurrentSkipListMap将这种表头Index定义为它的静态成员内部类HeadIndex。既然是特殊的Index,那么HeadIndex自然是Index的子类。该类的代码很短,全部代码如下:
1 | static final class HeadIndex<K,V> extends Index<K,V> { |
原文:大专栏 Java 并发-ConcurrentSkipListMap
标签:好的 == down ali 跳表 静态 构建 lan tun
原文地址:https://www.cnblogs.com/petewell/p/11601768.html