CopyOnWriteArraySet内部持有一个CopyOnWriteArrayList实例,通过手动检查元素是否存在(基于equals())实现去重,add/remove/contains均为O(n)时间复杂度,但遍历线程安全、读操作零同步。

CopyOnWriteArraySet 本身并不直接基于 CopyOnWriteArrayList 实现去重,而是**内部持有一个 CopyOnWriteArrayList 实例,并借助其线程安全的写时复制机制 + 手动去重逻辑来保证集合语义**。
底层用的是 CopyOnWriteArrayList,但去重靠自己判断
CopyOnWriteArraySet 的源码中有一个私有 final 成员:
private final CopyOnWriteArrayList它不是“复用 ArrayList 的去重能力”(List 本就不去重),而是所有添加、删除操作都委托给这个 al,但在 add() 等方法里**主动检查元素是否已存在**,仅当不存在时才真正调用 al.add()。
例如 add() 方法逻辑类似:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 先调用 al.indexOf(element) 判断是否已存在(返回 -1 表示没有)
- 如果不存在,再调用 al.add(element) —— 这一步会触发 CopyOnWriteArrayList 的写时复制:新建数组、拷贝旧数据、追加新元素
- 返回 true 表示新增成功;否则返回 false
为什么不用 HashSet 或其他结构?
因为 CopyOnWriteArraySet 的核心诉求是:在**遍历不加锁、写操作线程安全、适合读多写少场景**的前提下提供 Set 语义。HashSet 虽然去重快,但迭代器不是弱一致性,且写操作需同步;而 CopyOnWriteArrayList 天然支持安全迭代(始终基于快照),所以选它作底座。
代价是:add/remove 时间复杂度为 O(n),因为每次都要 scan 整个数组查重;但换来的是读操作零同步、迭代绝对安全。
去重依赖 equals(),不是 ==
和所有 Set 一样,CopyOnWriteArraySet 判重完全基于元素的 equals() 方法(同时要求 hashCode() 与 equals 一致)。它调用的是 al.indexOf(),而 indexOf 内部用的就是 equals() 逐个比较。
所以自定义类做元素时,必须正确重写 equals 和 hashCode,否则可能重复添加或查找不到。
写时复制 + 查重 = 安全但较慢的 Set
- 每次 add 都要遍历当前数组(O(n)),再执行一次写时复制(O(n) 拷贝)→ 总体 O(n)
- remove 同样先 indexOf 再 remove(index),也是 O(n)
- contains 直接调用 al.indexOf,O(n),但无需加锁,读并发极高
- 迭代器直接返回 al 的 iterator,返回的是快照,即使其他线程正在 add/remove,也不影响本次遍历

















