实际上,在JDK合同中有许多数据结构,最常用的是链表、哈希表等,除了这些数据结构外,还有一个特殊的数据结构跳表,大多数人可能不太了解,故本文主要介绍跳表的原理和在JDK中的实现。
跳表(Skip list)它是一种可以取代平衡树的方法随机化的数据结构,基于并联链表的默认情况是Key值升序。Skip list将已排序的数据分布在多层链表中,通过0-1随机数来决定一个数据是否向上攀升。“换取时间的空间”一种算法在每个节点中添加向前指针,在插入、删除和搜索时可以忽略一些不可能的结点,从而提高效率。
实际上,跳表是链表的一种,只是在链表的基础上增加了跳跃功能。正是这种跳跃功能使跳表能够在搜索元素时提供O(log n)时间复杂。搜索红黑树等平衡数据结构的时间复杂性也是O(log n),与红黑树相比,平衡二叉树skiplist的优势是更好地支持并发操作,但实现红黑树等数据结构并不容易,但只要您熟悉链表的基本操作,加上对跳表原理的理解,实现跳表数据结构是非常自然的。保持数据结构的平衡比保持数据结构的平衡要简单得多。对于大多数应用,使用Skip 与树算法相比,list相对简单。因为Skip list相对简单,更容易实现,尽管它与平衡树具有相同的时间复杂性(O(logn)),但是skip list的常数项将相对较小。Skip list也节省了空间。一个节点平均只需要1.333个指针(甚至更少)。
那么跳表的具体结构是什么呢?下图为跳表数据结构的原理图:
很明显,跳表是一种典型的以空间换时间的数据结构。跳表算法和哈希算法的另一个区别是:哈希算法不会保存数据的顺序,跳表中的所有元素都是排序的。因此,一组有序的数据将通过遍历跳表获得。
在这种方便的数据结构也用于JDK内部,比如:
ConcurrentSkipListMap,concurrentSkiplistset等。下面我们主要介绍ConcurrentSkiplistmap。说到ConcurentSkiplistmap,我们应该比较Hashmap,ConcurrentHashMap,concurrentSkiplistmap这三类来解释。它们都以键值对的方式存储数据。Hashmap是线程不安全的,而ConcurentHashmap和ConcurentSkiplistmap是线程安全的,它们都使用无锁CAS算法实现同步。concurenthashmap中的元素是无序的,concurentskiplistmap中的元素是有序的。三者的具体区别可参考具体资料,以下主要讲解ConcurentSkiplistmap的实现原理。
了解跳表的原理,不难理解ConcurentSkiplistmap的原理。它的内部有几个重要的数据结构。首先是Node。一个Node表示一个节点,它包含两个重要元素,Key和value,另一个Next指向Node的节点表示下一个节点。
对于所有Node方法都采用CAS方法,线程安全。
上述两种方法之一是设置value,二是设置next,具体实现可查看源代码。
另一个重要的数据结构是Index,从上面跳表的结构可以看出,我们需要知道一个元素在哪一层,哪个节点,所以我们需要Index来管理它。
在Node包装在Index中,并引用向右和向下。此外,数据结构也非常重要,HeadIndex,它记录了每层的表头在哪一层,它继承了Index。
上面简单介绍一下ConcurentSkiplistmap的重要内部数据结构,重点是了解跳表数据结构,理解跳表是掌握ConcurentSkiplistmap的关键。
跳表不仅可以提高搜索性能,还可以提高插入和删除操作的性能。我相信,在阅读了这篇文章后,我们对跳表有了更深的理解,使用这种数据结构更方便,可以在未来的编程和操作中使用。