copyonwritearrayset 不会出现重复元素,因其 add 方法调用底层 copyonwritearraylist 的 addifabsent,先遍历判断存在性(equals 比较),仅不存在时才插入新数组,整个过程原子且线程安全。

CopyOnWriteArraySet 是 Java 并发包中线程安全的 Set 实现,底层基于 CopyOnWriteArrayList,适合读多写少场景。它通过“写时复制”机制天然避免重复元素——因为每次 add 操作都会先检查是否已存在,不存在才插入,整个过程原子且线程安全。
为什么 CopyOnWriteArraySet 不会出现重复元素
它的 add 方法内部调用的是底层 CopyOnWriteArrayList 的 addIfAbsent,该方法会先遍历当前数组判断元素是否存在(使用 equals 比较),仅当不存在时才将新元素加入新数组。这个判断+插入的过程在写操作中是一次性完成的,不会被其他线程打断,因此无需额外同步就能保证 Set 语义(无重复)。
正确使用方式:直接 add 即可,无需手动去重
不需要像非线程安全集合那样加锁或双重检查。只要用它的标准 API,重复添加同一元素会自动忽略:
- 调用 set.add(element) —— 返回 true 表示新增成功,false 表示已存在
- 避免用 new CopyOnWriteArraySet(collection) 初始化含重复元素的集合,构造时不会去重(它只是把传入集合的元素逐个 add,而 add 本身会去重,所以最终结果仍是无重复的)
- 不要混用迭代器修改(如 remove)和并发写操作,虽然安全但可能看到旧快照;若需边遍历边清理,建议改用 ConcurrentHashMap + computeIfAbsent 等更灵活方案
注意它的适用边界和潜在问题
它不是万能替代品。高并发写场景下性能较差,因为每次 add 都要复制整个数组:
- 适合读操作远多于写操作(比如缓存白名单、监听器列表)
- 元素数量不宜过大(几百到几千较稳妥),否则复制开销明显
- add、remove 等写操作不保证实时对其他线程可见(因基于快照),如果业务依赖“立即可见”,需考虑其他方案如 ConcurrentSkipListSet
- equals 和 hashCode 必须正确定义,否则重复判断失效(和所有 Set 一样)
一个典型安全用法示例
比如维护一个动态的在线用户 ID 集合:
CopyOnWriteArraySet<string> onlineUsers = new CopyOnWriteArraySet();
// 多个线程同时调用
onlineUsers.add("user1"); // 第一次返回 true
onlineUsers.add("user1"); // 第二次返回 false,不重复添加
System.out.println(onlineUsers.size()); // 输出 1
</string>
无需 synchronized 或 ReentrantLock,也不用手动 contains + add 组合,一行代码就兼顾线程安全与去重。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











