问题

(1)ConcurrentSkipListSet的底层是ConcurrentSkipListMap吗?

(2)ConcurrentSkipListSet是线程安全的吗?

(3)ConcurrentSkipListSet是有序的吗?

(4)ConcurrentSkipListSet和之前讲的Set有何不同?

简介

ConcurrentSkipListSet底层是通过ConcurrentNavigableMap来实现的,它是一个有序的线程安全的集合。

源码分析

它的源码比较简单,跟通过Map实现的Set基本是一致,只是多了一些取最近的元素的方法。

为了保持专栏的完整性,我还是贴一下源码,最后会对Set的整个家族作一个对比,有兴趣的可以直接拉到最下面。

// 实现了NavigableSet接口,并没有所谓的ConcurrentNavigableSet接口
public class ConcurrentSkipListSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2479143111061671589L; // 存储使用的map
private final ConcurrentNavigableMap<E,Object> m; // 初始化
public ConcurrentSkipListSet() {
m = new ConcurrentSkipListMap<E,Object>();
} // 传入比较器
public ConcurrentSkipListSet(Comparator<? super E> comparator) {
m = new ConcurrentSkipListMap<E,Object>(comparator);
} // 使用ConcurrentSkipListMap初始化map
// 并将集合c中所有元素放入到map中
public ConcurrentSkipListSet(Collection<? extends E> c) {
m = new ConcurrentSkipListMap<E,Object>();
addAll(c);
} // 使用ConcurrentSkipListMap初始化map
// 并将有序Set中所有元素放入到map中
public ConcurrentSkipListSet(SortedSet<E> s) {
m = new ConcurrentSkipListMap<E,Object>(s.comparator());
addAll(s);
} // ConcurrentSkipListSet类内部返回子set时使用的
ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) {
this.m = m;
} // 克隆方法
public ConcurrentSkipListSet<E> clone() {
try {
@SuppressWarnings("unchecked")
ConcurrentSkipListSet<E> clone =
(ConcurrentSkipListSet<E>) super.clone();
clone.setMap(new ConcurrentSkipListMap<E,Object>(m));
return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
} /* ---------------- Set operations -------------- */
// 返回元素个数
public int size() {
return m.size();
} // 检查是否为空
public boolean isEmpty() {
return m.isEmpty();
} // 检查是否包含某个元素
public boolean contains(Object o) {
return m.containsKey(o);
} // 添加一个元素
// 调用map的putIfAbsent()方法
public boolean add(E e) {
return m.putIfAbsent(e, Boolean.TRUE) == null;
} // 移除一个元素
public boolean remove(Object o) {
return m.remove(o, Boolean.TRUE);
} // 清空所有元素
public void clear() {
m.clear();
} // 迭代器
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
} // 降序迭代器
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
} /* ---------------- AbstractSet Overrides -------------- */
// 比较相等方法
public boolean equals(Object o) {
// Override AbstractSet version to avoid calling size()
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
try {
// 这里是通过两次两层for循环来比较
// 这里是有很大优化空间的,参考上篇文章CopyOnWriteArraySet中的彩蛋
return containsAll(c) && c.containsAll(this);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
} // 移除集合c中所有元素
public boolean removeAll(Collection<?> c) {
// Override AbstractSet version to avoid unnecessary call to size()
boolean modified = false;
for (Object e : c)
if (remove(e))
modified = true;
return modified;
} /* ---------------- Relational operations -------------- */ // 小于e的最大元素
public E lower(E e) {
return m.lowerKey(e);
} // 小于等于e的最大元素
public E floor(E e) {
return m.floorKey(e);
} // 大于等于e的最小元素
public E ceiling(E e) {
return m.ceilingKey(e);
} // 大于e的最小元素
public E higher(E e) {
return m.higherKey(e);
} // 弹出最小的元素
public E pollFirst() {
Map.Entry<E,Object> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
} // 弹出最大的元素
public E pollLast() {
Map.Entry<E,Object> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
} /* ---------------- SortedSet operations -------------- */ // 取比较器
public Comparator<? super E> comparator() {
return m.comparator();
} // 最小的元素
public E first() {
return m.firstKey();
} // 最大的元素
public E last() {
return m.lastKey();
} // 取两个元素之间的子set
public NavigableSet<E> subSet(E fromElement,
boolean fromInclusive,
E toElement,
boolean toInclusive) {
return new ConcurrentSkipListSet<E>
(m.subMap(fromElement, fromInclusive,
toElement, toInclusive));
} // 取头子set
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
} // 取尾子set
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
} // 取子set,包含from,不包含to
public NavigableSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
} // 取头子set,不包含to
public NavigableSet<E> headSet(E toElement) {
return headSet(toElement, false);
} // 取尾子set,包含from
public NavigableSet<E> tailSet(E fromElement) {
return tailSet(fromElement, true);
} // 降序set
public NavigableSet<E> descendingSet() {
return new ConcurrentSkipListSet<E>(m.descendingMap());
} // 可分割的迭代器
@SuppressWarnings("unchecked")
public Spliterator<E> spliterator() {
if (m instanceof ConcurrentSkipListMap)
return ((ConcurrentSkipListMap<E,?>)m).keySpliterator();
else
return (Spliterator<E>)((ConcurrentSkipListMap.SubMap<E,?>)m).keyIterator();
} // 原子更新map,给clone方法使用
private void setMap(ConcurrentNavigableMap<E,Object> map) {
UNSAFE.putObjectVolatile(this, mapOffset, map);
} // 原子操作相关内容
private static final sun.misc.Unsafe UNSAFE;
private static final long mapOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = ConcurrentSkipListSet.class;
mapOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("m"));
} catch (Exception e) {
throw new Error(e);
}
}
}

可以看到,ConcurrentSkipListSet基本上都是使用ConcurrentSkipListMap实现的,虽然取子set部分是使用ConcurrentSkipListMap中的内部类,但是这些内部类其实也是和ConcurrentSkipListMap相关的,它们返回ConcurrentSkipListMap的一部分数据。

另外,这里的equals()方法实现的相当敷衍,有很大的优化空间,作者这样实现,应该也是知道几乎没有人来调用equals()方法吧。

总结

(1)ConcurrentSkipListSet底层是使用ConcurrentNavigableMap实现的;

(2)ConcurrentSkipListSet有序的,基于元素的自然排序或者通过比较器确定的顺序;

(3)ConcurrentSkipListSet是线程安全的;

彩蛋

Set大汇总:

Set 有序性 线程安全 底层实现 关键接口 特点
HashSet HashMap 简单
LinkedHashSet LinkedHashMap 插入顺序
TreeSet NavigableMap NavigableSet 自然顺序
CopyOnWriteArraySet CopyOnWriteArrayList 插入顺序,读写分离
ConcurrentSkipListSet ConcurrentNavigableMap NavigableSet 自然顺序

从中我们可以发现一些规律:

(1)除了HashSet其它Set都是有序的;

(2)实现了NavigableSet或者SortedSet接口的都是自然顺序的;

(3)使用并发安全的集合实现的Set也是并发安全的;

(4)TreeSet虽然不是全部都是使用的TreeMap实现的,但其实都是跟TreeMap相关的(TreeMap的子Map中组合了TreeMap);

(5)ConcurrentSkipListSet虽然不是全部都是使用的ConcurrentSkipListMap实现的,但其实都是跟ConcurrentSkipListMap相关的(ConcurrentSkipListeMap的子Map中组合了ConcurrentSkipListMap);


欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

死磕 java集合之ConcurrentSkipListSet源码分析——Set大汇总的更多相关文章

  1. 死磕 java集合之DelayQueue源码分析

    问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...

  2. 死磕 java集合之PriorityBlockingQueue源码分析

    问题 (1)PriorityBlockingQueue的实现方式? (2)PriorityBlockingQueue是否需要扩容? (3)PriorityBlockingQueue是怎么控制并发安全的 ...

  3. 死磕 java集合之PriorityQueue源码分析

    问题 (1)什么是优先级队列? (2)怎么实现一个优先级队列? (3)PriorityQueue是线程安全的吗? (4)PriorityQueue就有序的吗? 简介 优先级队列,是0个或多个元素的集合 ...

  4. 死磕 java集合之CopyOnWriteArraySet源码分析——内含巧妙设计

    问题 (1)CopyOnWriteArraySet是用Map实现的吗? (2)CopyOnWriteArraySet是有序的吗? (3)CopyOnWriteArraySet是并发安全的吗? (4)C ...

  5. 死磕 java集合之LinkedHashSet源码分析

    问题 (1)LinkedHashSet的底层使用什么存储元素? (2)LinkedHashSet与HashSet有什么不同? (3)LinkedHashSet是有序的吗? (4)LinkedHashS ...

  6. 死磕 java集合之ConcurrentHashMap源码分析(三)

    本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...

  7. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  8. 【死磕 Java 集合】— ConcurrentSkipListMap源码分析

    转自:http://cmsblogs.com/?p=4773 [隐藏目录] 前情提要 简介 存储结构 源码分析 主要内部类 构造方法 添加元素 添加元素举例 删除元素 删除元素举例 查找元素 查找元素 ...

  9. 死磕 java集合之LinkedList源码分析

    问题 (1)LinkedList只是一个List吗? (2)LinkedList还有其它什么特性吗? (3)LinkedList为啥经常拿出来跟ArrayList比较? (4)我为什么把LinkedL ...

随机推荐

  1. C++判断字符串是否为空的一个小问题

    刚才visual studio下处理一个函数字符串入参,判断入参字符串是否为空有点小问题. 接口函数声明是这样的:SHORT GETWFSINFPINKEYDETAIL(LPCTSTR strKeyN ...

  2. SOFA 源码分析 —— 过滤器设计

    前言 通常 Web 服务器在处理请求时,都会使用过滤器模式,无论是 Tomcat ,还是 Netty,过滤器的好处是能够将处理的流程进行分离和解耦,比如一个 Http 请求进入服务器,可能需要解析 h ...

  3. C++ 文件流的详解

    部分内容转载:http://blog.csdn.net/kingstar158/article/details/6859379 感谢追求执着,原本想自己写,却发现了这么明白的文章. C++文件流操作是 ...

  4. DataTime显示格式【转】

    随心所欲的DateTime显示格式 任何项目,难免会碰到DateTime的显示问题,.net框架虽提供丰富多样的显示方法,但我很少使用,因老忘记细节,每次都要纠结到底月份在前还是年份在前:日期分隔符到 ...

  5. 看完这篇Linux基本的操作就会了

    前言 只有光头才能变强 这个学期开了Linux的课程了,授课的老师也是比较负责任的一位.总的来说也算是比较系统地学习了一下Linux了~~~ 本文章主要是总结Linux的基础操作以及一些简单的概念~如 ...

  6. c语言中realloc()函数解析

    一.基本特性 1. realloc()函数可以重用或扩展以前用malloc().calloc()及realloc()函数自身分配的内存. 2. realloc()函数需两个参数:一个是包含地址的指针( ...

  7. python+selenium 环境搭建以及元素定位

    在给公司同事给培训了WEB自动化框架,现在和大家分享交流下

  8. python_汉塔诺

    '''据说古代有一个梵塔,塔内有三个底座A.B.C,A座上有64个盘子,盘子大小不等,大的在下,小的在上.有一个和尚想把这64个盘子从A座移到C座,但每次只能允许移动一个盘子,在移动盘子的过程中可以利 ...

  9. Spring Aop技术原理分析

    本篇文章从Aop xml元素的解析开始,分析了Aop在Spring中所使用到的技术.包括Aop各元素在容器中的表示方式.Aop自动代理的技术.代理对象的生成及Aop拦截链的调用等等.将这些技术串联起来 ...

  10. java客户端调用webService

    啥也不想说,以前使用的方法突然不行了.各种网搜(记得别忘记到jar包哦:axis.jar) 看代码,第一种方式,也就是以前的方式: 改方式不用表名参数名称 public static String i ...