问题

(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. linux定时清理数据库过期记录

    cron服务是Linux的内置服务,但它不会开机自动启动.可以用以下命令启动和停止服务: /sbin/service crond start//没打开的话首先要打开. /sbin/service cr ...

  2. springAOP之代理

    AOP是指面向切面编程. 在学习AOP之前先来了解一下代理,因为传说中的AOP其实也对代理的一种应用. 首先来看这样一段代码: public interface Hello { void say(St ...

  3. (二)SpringBoot基础篇- 静态资源的访问及Thymeleaf模板引擎的使用

    一.描述 在应用系统开发的过程中,不可避免的需要使用静态资源(浏览器看的懂,他可以有变量,例:HTML页面,css样式文件,文本,属性文件,图片等): 并且SpringBoot内置了Thymeleaf ...

  4. [ SSH框架 ] Struts2框架学习之三(OGNl和ValueStack值栈学习)

    一.OGNL概述 1.1 什么是OGNL OGNL的全称是对象图导航语言( object-graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可 ...

  5. DX11 Without DirectX SDK--04 使用DirectX Tool Kit帮助开发

    回到 DirectX11--使用Windows SDK来进行开发 DirectX Tool Kit下载 DirectX Tool Kit是一个包含许多类的集合,用于为公共Windows平台编写Dire ...

  6. webpack bug及解决方案

    1.webpack打包后z-index失效 解决方案:z-index设置成行内样式,例如:root.style.cssText = 'z-index:100000 !important;';

  7. vim折叠快捷键

    参考:http://www.cnblogs.com/fakis/archive/2011/04/14/2016213.html 1. 折叠方式 可用选项来设定折叠方式: 可在Vim 配置文件中设置 s ...

  8. div布局之面向对象

    栗子之导航条(navbar) http://www.runoob.com/try/try2.php?filename=bootstrap-using-glyphicons-navbar <!DO ...

  9. linux ulimit 调优

    概要:linux系统默认open files数目为1024, 有时应用程序会报Too many open files的错误,是因为open files 数目不够.这就需要修改ulimit和file-m ...

  10. Day2 《机器学习》第二章学习笔记

    这一章应该算是比价了理论的一章,我有些概率论基础,不过起初有些地方还是没看多大懂.其中有些公式的定义和模型误差的推导应该还是很眼熟的,就是之前在概率论课上提过的,不过有些模糊了,当时课上学得比较浅. ...