在Java集合框架中,ArrayList和CopyOnWriteArrayList都实现了List接口,但在底层结构和并发表现上差别很大。理解它们的不同,能帮你在单机多线程或高并发读取场景中写出更稳的代码。

ArrayList的基本特点
ArrayList内部用一个可扩容的数组来存元素。读操作通过下标直接访问,时间复杂度是O(1)。写操作在尾部添加一般是O(1),但触发扩容或中间插入删除就会搬移数据。
它没有任何同步控制,所以在多线程同时修改时会出现数据错乱、数组越界甚至抛出ConcurrentModificationException。如果在并发环境使用,需要外部加锁或者用Collections.synchronizedList包装。
ArrayList简单示例
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
// 多线程下这样直接修改有风险
System.out.println(list.get(0));
}
}
CopyOnWriteArrayList的基本特点
CopyOnWriteArrayList位于java.util.concurrent包下。它的核心思路是:每次修改(add、set、remove)都会复制一份底层数组,在新数组上完成修改,最后把引用指向新数组。读操作始终访问旧数组,不需要加锁。
因此读操作非常轻量且线程安全,适合读多写少的业务,比如监听器列表、配置缓存。但写操作因为要拷贝数组,开销随数据量上升而变大,且读可能短暂看到旧数据(最终一致)。
CopyOnWriteArrayList简单示例
import java.util.concurrent.CopyOnWriteArrayList;
public class COWDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<>();
cow.add("x");
// 多个线程读不会阻塞
System.out.println(cow.get(0));
}
}
两者主要差异对比
| 对比项 | ArrayList | CopyOnWriteArrayList |
|---|---|---|
| 线程安全 | 否 | 是(读无锁) |
| 写开销 | 低,可能扩容 | 高,需拷贝数组 |
| 读性能 | 高 | 高且无锁 |
| 数据一致性 | 强一致(单线程) | 弱一致(最终一致) |
| 适用场景 | 单线程或外部同步 | 读多写少并发 |
怎么选
如果你的代码只在单线程跑,或者修改和读取都由同一把锁保护,用ArrayList更省内存和CPU。当多个线程频繁读、很少改,并且能接受短时间读到旧值,CopyOnWriteArrayList会减少锁竞争、避免并发异常。
注意不要把它用在写频繁的地方,比如每秒成千上万次更新,拷贝数组会拖垮性能。那种情况可以考虑ConcurrentHashMap或分片锁方案。
小结
ArrayList和CopyOnWriteArrayList并不是替代关系,而是面向不同并发模型的实现。看清读写比例和一致性要求,才能把Java集合框架用对地方。
CopyOnWriteArrayListArrayListJava_collection修改时间:2026-07-26 10:21:25