Java遍历集合应避免ConcurrentModificationException:增强for循环仅适用于只读场景,删除须用Iterator.remove();多线程需线程安全集合或加锁;遍历时添加元素应先缓存后批量处理。

Java中遍历集合最常用的是迭代器(Iterator)和增强for循环(for-each),但若操作不当,容易触发ConcurrentModificationException或漏遍历、重复遍历等问题。关键在于理解二者底层机制差异,并避开修改集合结构的“雷区”。
增强for循环适合只读遍历
增强for循环本质是编译器自动展开为迭代器调用,语法简洁但隐藏了迭代器对象。它适用于纯粹访问元素、不做结构性修改的场景。
- 内部使用
hasNext()和next(),不暴露迭代器引用,无法调用remove() - 若在循环体内直接调用集合的
add()、remove()、clear()等方法,会触发并发修改异常 - 示例:以下代码会抛出异常
for (String s : list) {
if ("abc".equals(s)) {
list.remove(s); // 运行时抛出 ConcurrentModificationException
}
}
需要删除元素时必须用迭代器的remove()方法
迭代器的remove()是唯一安全的删除方式,它与当前迭代状态同步,能维护内部游标一致性。
- 必须在调用
next()之后、且仅调用一次remove(),否则抛出IllegalStateException - 不能在同一次迭代中多次调用
remove() - 支持边遍历边删除,无需额外缓存待删元素
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("abc".equals(s)) {
it.remove(); // 安全删除
}
}
多线程环境下需额外加锁或选用线程安全集合
即使单线程下用迭代器删除是安全的,多线程共享同一集合时,仍可能因其他线程修改结构而失败。
立即学习“Java免费学习笔记(深入)”;
-
ArrayList、HashMap等非线程安全集合,其迭代器是“快速失败”(fail-fast)的 - 可改用
CopyOnWriteArrayList(适合读多写少)或ConcurrentHashMap(支持并发遍历) - 若必须用普通集合,可在遍历时对整个集合加同步锁(注意粒度,避免阻塞过久)
遍历时添加元素要格外谨慎
无论用哪种方式,遍历中向集合添加新元素几乎总是危险的——增强for循环和普通迭代器都不支持添加操作。
- 迭代器没有
add()方法;调用集合自身的add()会破坏迭代器状态 - 若确实需要边遍历边添加,建议先收集新增元素,遍历结束后统一
addAll() - 某些集合如
LinkedList的ListIterator支持add(),但仅限双向迭代且需注意索引偏移
List<String> toAdd = new ArrayList<>();
for (String s : list) {
if (s.length() > 5) {
toAdd.add(s.toUpperCase());
}
}
list.addAll(toAdd); // 遍历完成后再批量添加


















