ConcurrentSkipListMap,属于并发集合类,来源于大名鼎鼎的J.U.C,集合并发类的要求是执行速度快,提取数据准,最著名的类便是之前有接触到的ConcurrentHashMap类,通过不断的优化,由刚开始的锁分段到后来的CAS,不断地去提高自身的并发性能,其他便是ConcurrentSkipListMap,CopyOnWriteArrayList,BlockingQueue,虽然使用的频率没有AVL或者红黑树那么高,但是它的实现相对于树来说更加简单。
ConcurrentSkipListMap可以看成是并发版本的TreeMap,和TreeMap不同是,TreeMap不是线程安全的,ConcurrentSkipListMap是线程安全有序的哈希表,且不是基于红黑树实现的,其底层是一种类似跳表(Skip List)的结构
跳表(Skip List)
Skip List(以下简称跳表),是一种类似链表的数据结构,其查询/插入/删除的时间复杂度都是O(logn)
。
我们知道,通常意义上的链表是不能支持随机访问的(通过索引快速定位),其查找的时间复杂度是O(n)
,而数组这一可支持随机访问的数据结构,虽然查找很快,但是插入/删除元素却需要移动插入点后的所有元素,时间复杂度为O(n)
。
首先我们来看下传统链表
如果我想查询15这个元素,那么我需要从头开始遍历,知道遍历到我想要的数据停止,查找的时间复杂度为O(n)
来看下Skip List的数据结构是什么样的:
上图便为跳表的一种可能的数据结构,如果说我要查询15这个元素
有种类似于二分法查找的感觉,事实上没错,跳表的查询也是基于二分法查询的概念
所以跳表总结下来的性质有以下几点
(1) 由很多层结构组成,层数是通过一定的概率随机产生的;
(2) 每一层都是一个有序的链表,默认是升序 ;
(3) 最底层的链表包含所有元素;
(4) 如果一个元素出现在i层的链表中,则它在i层之下的链表也都会出现;
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。
接下来我们从源码来看
doput()获取方法
1 | private V doPut(K kkey, V value, boolean onlyIfAbsent) { |
doRemove()方法
1 | final V doRemove(Object okey, Object value) { |
在有序的情况下
有序的情况下:
- 在非多线程的情况下,应当尽量使用TreeMap(红黑树实现)。
- 对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。但是对于高并发程序,应当使用ConcurrentSkipListMap。
无序情况下:
- 并发程度低,数据量大时,ConcurrentHashMap 存取远大于ConcurrentSkipListMap。
- 数据量一定,并发程度高时,ConcurrentSkipListMap比ConcurrentHashMap效率更高。
参考