数据结构-跳表(基于JAVA的实现)
跳表的原理与特点
跳表实质就是一种可以进行二分查找的有序链表,跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。解决了链表查询慢的问题,但会占有更多的内存,是一种以空间换内存的数据结构。
查询任意数据的时间复杂度O(logn)
插入数据的时间复杂度O(logn)
空间复杂度:O(n)
跳跃表应用场景分析
在Server端,对并发和性能有要求的情况下,如何选择合适的数据结构(跳表和红黑树)?
如果单纯比较性能,跳跃表和红黑树可以说相差不大,但是加上并发的环境就不一样了,如果要更新数据,跳表需要更新的部分就比较少,锁的东西也就比较少,所以不同线程争锁的代价就相对少了。而红黑树有个平衡的过程,牵涉到大量的节点,争锁的代价也就相对较高了。性能也就不如前者了。
在并发环境下Skip List有另外一个优势,红黑树在插入和删除的时候可能需要做一些rebalance的操作,这样的操作可能会涉及到整个树的其他部分,而Skip List的操作显然更加局部性一些,锁需要盯住的节点更少,因此在这样的情况下性能好一些。
在Redis中,有序集数据类型(Sorted Set)也是用跳表实现的。
Redis作者描述的使用跳表的原因:
1、跳表的一个缺点是耗内存(因为要重复分层存节点),但是作者也说了,可以调参数来降低内存消耗,和那些平衡树结构达到差不多。
2、redis经查有范围操作,这样利用跳表里面的双向链表,可以方便地操作。另外还有缓存区域化(cache locality)不会比平衡树差。
3、实现简单。zrank操作能够到O(log(N))。
跳跃表的JAVA应用
在Java的API中已经有了实现:
- ConcurrentSkipListMap. 在功能上对应HashTable、HashMap、TreeMap。
- ConcurrentSkipListSet . 在功能上对应HashSet.
确切来说,Skip List更像Java中的TreeMap。TreeMap基于红黑树(一种自平衡二叉查找树)实现的,时间复杂度平均能达到O(log n),TreeMap输出是有序的,ConcurrentSkipListMap和ConcurrentSkipListSet 输出也是有序的(本博测试过)。下例的输出是从小到大,有序的。
使用样例
import java.util.*;
import java.util.concurrent.*;
/*
* 跳表(SkipList)这种数据结构算是以前比较少听说过,它所实现的功能与红黑树,AVL树都差不太多,说白了就是一种基于排序的索引结构,
* 它的统计效率与红黑树差不多,但是它的原理,实现难度以及编程难度要比红黑树简单。
* 另外它还有一个平衡的树形索引机构没有的好处,这也是引导自己了解跳表这种数据结构的原因,就是在并发环境下其表现很好.
* 这里可以想象,在没有了解SkipList这种数据结构之前,如果要在并发环境下构造基于排序的索引结构,那么也就红黑树是一种比较好的选择了,
* 但是它的平衡操作要求对整个树形结构的锁定,因此在并发环境下性能和伸缩性并不好.
* 在Java中,skiplist提供了两种:
* ConcurrentSkipListMap 和 ConcurrentSkipListSet
* 两者都是按自然排序输出。
*/
public class SkipListDemo {
public static void skipListMapShow(){
Map<Integer,String> map= new ConcurrentSkipListMap<>();
map.put(1, "1");
map.put(23, "23");
map.put(