Java源码分析系列笔记-11.CopyOnWriteArraySet
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的更多相关文章
- Java源码分析系列之HttpServletRequest源码分析
从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...
- Java源码分析系列
1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...
- spring源码分析系列 (8) FactoryBean工厂类机制
更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...
- Spring mvc源码分析系列--Servlet的前世今生
Spring mvc源码分析系列--Servlet的前世今生 概述 上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑.今天暂且来填一个小坑,这篇文章我们 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
- jquery2源码分析系列
学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...
- [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat
概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
随机推荐
- 从零开始:基于 PyTorch 的图像分类模型
摘要:本文详细记录了使用 PyTorch 从零搭建一个图像分类模型的过程,涵盖卷积神经网络(CNN).数据预处理.模型设计.训练调试与优化.通过对 CIFAR-10 数据集的处理实践,结合经典文献和 ...
- linux下nacos集群部署报错Error: Could not create the Java Virtual Machine.
修改startup.sh时注意空格 注意这里-Dserver "-"之后没有空格 nohup $JAVA -Dserver.port=${PORT} ${JAVA_OPT} nac ...
- 【Win32】VC6 Visual C/C++ 6.0 修改程序图标
零.需求 就想给自己的C程序加个图标,好看些 一.解决 1.操作步骤 1.新建一个资源脚本 2.在新建的脚本上右键,选择插入 3.选择Icon,点新建或者引入,如果你没有准备图标点新建,有的话直接点引 ...
- Oracle chr() ascii()
函数简介 实用函数 chr() 和 ascii() chr() 函数将ASCII码转换为字符: ASCII码 –> 字符: ascii() 函数将字符转换为ASCII码: 字符 –> AS ...
- ChatGTP获取的d读取excel通用程序。
procedure ReadExcelFile(const FileName: string; AMemtable: TFDmemtable); var ExcelApp: OleVariant; S ...
- AI团队比单打独斗强!CrewAI多智能体协作系统开发踩坑全解析
AI团队比单打独斗强!CrewAI多智能体协作系统开发踩坑全解析 阅读时间: 5分钟 | 字数: 1500+ "你是否曾为单个大模型难以解决复杂专业问题而苦恼?是否想过,如果能像组建专业团队 ...
- 没错,Go 语言的函数参数没有引用传递方式
这篇文章想浅浅地讲解 Go 语言函数参数传递的值拷贝. 一句话观点 Go语言中所有传递都是值传递,严格来说并不存在引用传递的概念.传递指针只是传递指针的值,并不是引用传递,只不过通过指针可以间接修改变 ...
- 容器镜像基础(nydus)
" 包括 nydus 的基本概念.架构和容器镜像加速使用方法. 1. nydus 1.1 存在的问题 对于容器镜像使用者 问题一:启动容器慢:容器启动慢的情况普遍发生在当用户启动一个很大的容 ...
- ESP32教程:通过WIFI控制LED灯的开关
LED闪烁 在通过WIFI控制LED灯的开关之前,我们先实现一下LED闪烁. 接线图: 来源:https://esp32io.com/tutorials/esp32-led-blink 我的接线图: ...
- Marmoset Toolbag 4.02 八猴渲染器破解版 免费下载
猴安装包下载链接 https://pan.baidu.com/s/1Mgy3Mrlrb3Tvtc8w7Zn1nA?pwd=6666 提取码:6666 Marmoset Toolbag是由Monkey公 ...