题目
如何安全地从ArrayList中删除特定元素?
信息
- 类型:问答
- 难度:⭐
考点
ArrayList遍历, 迭代器使用, 并发修改异常
快速回答
安全删除ArrayList元素的两种主要方式:
- 使用Iterator的remove()方法:在遍历过程中安全删除
- 使用Java 8+的removeIf()方法:简洁高效的单行解决方案
避免在遍历时直接调用ArrayList的remove()方法,否则会抛出ConcurrentModificationException。
解析
原理说明
ArrayList是非线程安全的集合,当在遍历过程中直接修改结构(增删元素)时,会破坏迭代器的内部状态,导致抛出ConcurrentModificationException。这是因为迭代器会检查集合的modCount(修改次数)是否与初始值一致。
代码示例
// 错误示例:直接调用remove()导致异常
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
if ("B".equals(s)) {
list.remove(s); // 抛出ConcurrentModificationException
}
}
// 正确方案1:使用Iterator
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)); // 单行解决最佳实践
- 优先使用removeIf():代码简洁且性能高效(内部使用迭代器实现)
- 需要复杂逻辑时用Iterator:可在遍历中执行多步操作
- 避免索引遍历删除:倒序for循环虽可行,但易出错且不直观
常见错误
- foreach循环中调用remove():触发并发修改异常
- 正序索引遍历删除:删除后索引错位导致漏删或越界
- 多线程未同步:多线程操作需用
Collections.synchronizedList或CopyOnWriteArrayList
扩展知识
- fail-fast机制:ArrayList迭代器通过
expectedModCount检测并发修改 - 替代方案:
CopyOnWriteArrayList允许遍历时修改(牺牲写性能) - Java流式处理:结合
filter()和collect()创建新集合实现删除效果