1. 是什么

写时复制的set,有序不重复,底层使用CopyOnWriteArrayList实现

2. 如何使用

public class CopyOnWriteArraySetTest
{
public static void main(String[] args)
{
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>(); set.add("a");
set.add("b");
set.add("c"); set.remove("a");
System.out.println(set.contains("a"));//false System.out.println(Thread.currentThread().getName() + "读取set:" + set);//[b, c] new Thread(()->{
System.out.println(Thread.currentThread().getName() + "读取set:" + set);//[c]
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "再次读取set:" + set);//[c] }).start();
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "读取set:" + set);//[c]
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "再次读取set:" + set);//[c]
}).start(); try
{
TimeUnit.SECONDS.sleep(1);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
set.remove("b"); System.out.println(Thread.currentThread().getName() + "读取修改后的set:" + set);//[c] }
}

3. 原理分析

3.1. 构造方法

3.1.1. 底层使用CopyOnWriteArrayList实现

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
//底层使用的是CopyOnWriteArrayList实现
private final CopyOnWriteArrayList<E> al; public CopyOnWriteArraySet() {
//默认一个元素的数组
al = new CopyOnWriteArrayList<E>();
}
}

3.2. add方法

public boolean add(E e) {
//调用CopyOnWriteArrayList addIfAbsent方法
return al.addIfAbsent(e);
}

3.2.1. 转调CopyOnWriteArrayList addIfAbsent

  • CopyOnWriteArrayList addIfAbsent
public boolean addIfAbsent(E e) {
//获取原数组
Object[] snapshot = getArray();
//如果在数组中已经存在,那么返回false
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
//元素还不在数组中,则加入
addIfAbsent(e, snapshot);
}
  • 3行:获取原数组
  • 5行:遍历数组是否存在该object
  • 7行:不在的话插入list尾部

下面分别说明:

3.2.1.1. 遍历数组是否存在该object
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
//object为null
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
//object不为null
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
3.2.1.2. 不在的话则加锁插入list尾部
  • CopyOnWriteArrayList addIfAbsent
//把e加入到snapshot数组中
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
//原数组跟现在的数组不一样了
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
//遍历改变后的数组左边
for (int i = 0; i < common; i++)
//有一个位置不相同 且 改变后的数组这个位置xx的元素与要添加的元素相同,说明已经存在,返回false
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
//从改变后的数组右边查看是否存在要添加的元素
if (indexOf(e, current, common, len) >= 0)
return false;
}
//复制原数组并扩容,把e加入到数组的末尾,set回array
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

大体逻辑跟10.CopyOnWriteArrayList.md的remove方法差不多

3.3. contains方法

public boolean contains(Object o) {
//转调CopyOnWriteArrayList的contains方法
return al.contains(o);
}

3.3.1. 转调CopyOnWriteArrayList的contains

  • CopyOnWriteArrayList的contains
//CopyOnWriteArrayList的contains方法
public boolean contains(Object o) {
Object[] elements = getArray();
//遍历数组查找
return indexOf(o, elements, 0, elements.length) >= 0;
}
3.3.1.1. 遍历数组判断是否存在该object
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
//object为null
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
//object不为null
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}

3.4. remove方法

  • remove
public boolean remove(Object o) {
//转调CopyOnWriteArrayList的remove方法
return al.remove(o);
}

3.4.1. 转调CopyOnWriteArrayList的remove方法

  • CopyOnWriteArrayList remove
//CopyOnWriteArrayList的remove方法
public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
  • 3行:获取原数组
  • 4行:遍历数组是否存在该object
  • 5行:存在的话调用CopyOnWriteArrayList的remove方法删除
3.4.1.1. 遍历底层数组判断是否存在该object
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
//object为null
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
//object不为null
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
3.4.1.2. 存在的话再去删除

参考10.CopyOnWriteArrayList.md remove方法

4. 总结

底层直接通过调用CopyOnWriteArrayList实现的,因此有序

5. 参考

Java源码分析系列笔记-11.CopyOnWriteArraySet的更多相关文章

  1. Java源码分析系列之HttpServletRequest源码分析

    从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...

  2. Java源码分析系列

    1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...

  3. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  4. Spring mvc源码分析系列--Servlet的前世今生

    Spring mvc源码分析系列--Servlet的前世今生 概述 上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑.今天暂且来填一个小坑,这篇文章我们 ...

  5. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  6. MyCat源码分析系列之——结果合并

    更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...

  7. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  8. jquery2源码分析系列

    学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...

  9. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  10. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

随机推荐

  1. BUUCTF---old flashion

    1.题目 2.知识 3.解题 很奇怪,一段英文字母,看起来像维吉尼亚,但需要key,不知道什么是Key,我们丢到q爆破中试试 直接得出来了flag:flag{n1_2hen-d3_hu1-mi-ma_ ...

  2. 史上最全EffectiveJava总结(二)

    方法 49.检查参数的有效性 每次编写方法或构造函数时,都应该考虑参数存在哪些限制,并在文档中记录下来,然后在方法的开头显式地检查. 如果没有在方法开头就验证参数,可能会违反故障原子性.因为方法可能会 ...

  3. static修饰成员变量的特点及static修饰成员变量内存图解-java se进阶 day01

    1.static介绍 static是静态的意思,它可以用于修饰成员变量和成员方法 2.static的特点 1.被static修饰了的成员变量,可以被类中的所有对象所共享 虽然stu02没有给schoo ...

  4. 【UWB】DWM1000 室内定位串口协议说明

    UWB室内定位串口协议说明 通过串口发送的测距报告消息有三种: "mr"信息包括标签到锚定原始范围 "mc"标签到锚定范围偏差修正范围 - 用于标签位置 &qu ...

  5. IP地址查询服务

    IP地址查询站点 https://ip.cn/ http://ip.qq.com/ http://ip138.com/ https://www.apnic.net/ ... IP计算 ip地址在线计算 ...

  6. ZKmall模版商城前后端分离秒级响应架构深度解析

    在当今的电商领域,用户体验和响应速度已成为决定平台竞争力的关键因素.ZKmall模版商城,作为一款高性能的电商平台解决方案,通过采用前后端分离架构,实现了秒级响应,为用户带来了极致的购物体验.本文将深 ...

  7. 航天信息诺税通SAAS接口封装DLL

    项目中需要对接航天信息的诺税通接口开具电子发票,为此将功能封装到了DLL中,其他项目也可以方便的引用. Delphi调用示例: 有需要可以和我联系:yzqnet(微信)

  8. 等待元素加载出来后再执行下一步的方法(execute javascript指令的用法)

    上图,会员修改参数后,提示修改成功,弹出层会暂时冻结页面,导致"会员"菜单不可点击 除了使用sleep加等待时间的方法解决,本教程用"execute javascript ...

  9. live555开发笔记(二):live555创建RTSP服务器源码剖析,创建rtsp服务器的基本流程总结

    前言   基于Live555的流媒体服务器方案源码剖析,了解基本的代码搭建步骤.   Demo    关于.h264与.265      没深入研究,但是h264的后缀名.264替换为.h264文件, ...

  10. 图解-六种常见开源协议的比较(GPL,Mozilla,LGPL,BSD,Apache,MIT)

    附: 五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)