在 Java 中,ConcurrentModificationException(并发修改异常)遍历集合时最常见的错误之一。它发生在迭代过程中直接修改集合结构(添加/删除元素)时,与是否多线程无关。以下是详细的处理方案:


一、异常触发场景

List<
String> list =
new ArrayList<
>(Arrays.asList("A"
, "B"
, "C"
)
)
;
// 会抛出 ConcurrentModificationException
for (String s : list) {
if ("B".equals(s)
) {
list.remove(s)
;
// 直接调用集合的 remove()
}
}

二、解决方案(单线程环境)

1. 使用 Iterator.remove()(推荐)
Iterator<
String> iterator = list.iterator(
)
;
while (iterator.hasNext(
)
) {
String s = iterator.next(
)
;
if ("B".equals(s)
) {
iterator.remove(
)
;
// 安全删除当前元素
}
}
2. Java 8+ 使用 removeIf()
list.removeIf(s ->
"B".equals(s)
)
;
// 一行代码解决
3. 使用 CopyOnWriteArrayList(读多写少场景)
List<
String> list =
new CopyOnWriteArrayList<
>(Arrays.asList("A"
, "B"
, "C"
)
)
;
for (String s : list) {
if ("B".equals(s)
) {
list.remove(s)
;
// 安全但性能较低(每次修改复制整个数组)
}
}
4. 遍历时记录待删元素,遍历后统一删除
List<
String> toRemove =
new ArrayList<
>(
)
;
for (String s : list) {
if ("B".equals(s)
) {
toRemove.add(s)
;
}
}
list.removeAll(toRemove)
;
5. 使用下标遍历(仅适用于 ArrayList 等随机访问集合)
for (
int i = 0
; i < list.size(
)
; i++
) {
String s = list.get(i)
;
if ("B".equals(s)
) {
list.remove(i)
;
i--
;
// 修正索引
}
}

三、解决方案(多线程环境)

1. 使用并发集合类
// 线程安全的 List
List<
String> list = Collections.synchronizedList(
new ArrayList<
>(
)
)
;
// 遍历时需手动同步
synchronized (list) {
Iterator<
String> it = list.iterator(
)
;
while (it.hasNext(
)
) {
String s = it.next(
)
;
// 操作元素
}
}
2. 使用 java.util.concurrent 包中的集合
// 高性能并发 List
List<
String> list =
new CopyOnWriteArrayList<
>(
)
;
// 并发 Map
Map<
String
, String> map =
new ConcurrentHashMap<
>(
)
;
3. 使用锁机制
List<
String> list =
new ArrayList<
>(
)
;
ReentrantLock lock =
new ReentrantLock(
)
;
// 写操作加锁
lock.lock(
)
;
try {
list.add("X"
)
;
}
finally {
lock.unlock(
)
;
}
// 遍历时加锁
lock.lock(
)
;
try {
for (String s : list) {
/* ... */
}
}
finally {
lock.unlock(
)
;
}

四、关键预防原则

  1. 禁止在遍历中直接修改集合
    使用 iterator.remove() 是唯一安全的修改方式。

  2. 多线程环境优先选并发集合
    ConcurrentHashMap > Collections.synchronizedMap()
    CopyOnWriteArrayList > Collections.synchronizedList()

  3. Java 8+ 优先使用 Stream API

    list = list.stream(
    )
    .filter(s ->
    !"B".equals(s)
    )
    .collect(Collectors.toList(
    )
    )
    ;
  4. 避免在迭代中调用会修改集合的方法
    包括:add(), remove(), clear(), addAll() 等。


五、常见误区

方案问题推荐替代
Collections.synchronizedList()遍历时不同步仍会抛异常遍历时加锁或用 CopyOnWriteArrayList
for 循环删除元素ArrayList 删除后索引错位Iterator.remove() 或倒序遍历
多线程用普通集合 + 同步块易遗漏同步导致并发问题直接使用 ConcurrentHashMap

最佳实践:单线程用 Iterator.remove()removeIf(),多线程用并发集合类,Java 8+ 可配合 Stream API 实现无迭代修改。

posted on 2025-10-08 11:05  ycfenxi  阅读(6)  评论(0)    收藏  举报