今天看来一下Java中list集合部分的八股,发现了一个以前没注意过的问题,记录一下
list可以一边遍历一边修改元素吗?
答:在 Java 中,List在遍历过程中是否可以修改元素取决于遍历方式和具体的List实现类。
①:对于普通for循环,只要索引不超过list范围,可以一边遍历一遍修改
②:对于foreach循环,如果想边遍历边修改则必须使用迭代器的remove或set方法实现,否则会抛出异常。解释如下:
首先,需要知道的是,foreach循环是基于迭代器实现的,而在迭代器中有这样一个机制,即“快速失败” (Fail-Fast)机制
快速失败 (Fail-Fast) 机制
这里就提到了一个新的东西叫modCount,这里简单介绍一下:
modCount
(Modification Count):
代码示例
import java.util.ArrayList;
import java.util.List;public class FailFastExample {public static void main(String[] args) {// 1. 创建一个ArrayList并添加元素List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");names.add("Charlie");// 2. 使用foreach循环遍历 (底层使用迭代器)for (String name : names) { // Iterator<String> it = names.iterator();while(it.hasNext()) ...System.out.println(name);// 3. 在迭代过程中,尝试通过集合本身的remove方法删除元素 (错误的做法!)if (name.equals("Bob")) {names.remove("Bob"); // 结构性修改! modCount++!}}}
}
对于这段代码的解释:
关键总结
modCount
是集合的“修改计数器”。 结构性修改会递增它。- 迭代器创建时“拍照”记录
expectedModCount
。 它期望在迭代过程中,集合的modCount
不应该改变(除非是迭代器自己的remove()
方法,该方法会同步更新expectedModCount
)。 next()
(和hasNext()
)检查一致性。 每次调用next()
时,都会检查modCount == expectedModCount
。- 外部修改导致计数不一致。 如果在迭代过程中,通过集合自身的方法(如
list.add/remove
,map.put/remove
)而非迭代器的remove()
方法对集合进行了结构性修改,modCount
就会改变,而迭代器的expectedModCount
不变。 - 检测到不一致立即抛出
ConcurrentModificationException
。 这就是“快速失败”的表现。 foreach
循环依赖迭代器。 因此foreach
循环天生就带有这种快速失败的特性。- 避免异常: 如果需要在迭代中删除元素,必须使用迭代器自身的
remove()
方法。这个方法在删除元素后,会同步更新expectedModCount
为新的modCount
,从而保持一致性。