题目
ArrayList 的遍历与元素删除
信息
- 类型:问答
- 难度:⭐
考点
ArrayList遍历,迭代器使用,并发修改异常
快速回答
在遍历 ArrayList 时安全删除元素的方法:
- 使用 Iterator 的 remove() 方法可避免 ConcurrentModificationException
- 或使用 for 循环倒序遍历删除防止索引错位
- 禁止在 foreach 循环中直接调用 ArrayList 的 remove()
问题背景
在 Java 中直接使用 foreach 循环或迭代器遍历 ArrayList 时调用 remove() 删除元素,会抛出 ConcurrentModificationException。这是因为集合的结构被意外修改导致遍历状态不一致。
原理说明
ArrayList 内部维护一个 modCount(修改计数器)。当创建迭代器时,会记录当前 modCount 值。每次遍历会检查该值是否变化,若变化则抛出异常。直接调用集合的 remove() 会使 modCount++,但迭代器不知情。
正确做法与代码示例
方法 1:使用 Iterator.remove()
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("B".equals(item)) {
iterator.remove(); // 安全删除
}
}
// 结果:[A, C]方法 2:倒序 for 循环
for (int i = list.size() - 1; i >= 0; i--) {
if ("B".equals(list.get(i))) {
list.remove(i); // 倒序删除不影响索引
}
}常见错误
- foreach 循环中直接删除:
for (String s : list) { list.remove(s); }→ 抛出异常 - 正序 for 循环删除:
for (int i=0; i<list.size(); i++) { list.remove(i); }→ 漏删元素(删除后索引前移)
最佳实践
- 优先使用
Iterator.remove(),它是集合框架的标准安全操作 - Java 8+ 推荐:
list.removeIf(item -> "B".equals(item)); - 若需在遍历时同时删除/添加,考虑使用
CopyOnWriteArrayList
扩展知识
- ConcurrentModificationException 机制:所有 fail-fast 集合(如 ArrayList、HashMap)都通过 modCount 检测并发修改
- Java 8+ 简化操作:
removeIf(Predicate)内部使用迭代器实现,代码更简洁 - 替代方案:需要频繁增删时,可考虑
LinkedList(但迭代器删除仍是首选)