fori删除
fori 删除导致的异常
会报 indexOutBoundsException
fori循环是通过获取list中object[]数组的下标来循环获取数据的。一旦执行 list.remove(i) List中数组的数据
Object[] elementData 会执行 copy操作,数组index之后的所有数据会往前挪一位,同时把记录数组数量的字段size -1。
在这种情况下,当 i
为 2 时,list.remove(i)
会移除元素 "a"
,然后 ArrayList
会自动将后续元素前移并更新 size
,使得原本在索引位置 2 的元素被移除,导致当前的 i
指向了下一个元素,但我们仍然继续访问原来的 i
索引位置,最终触发 IndexOutOfBoundsException
。
csharp
List<String> list = new ArrayList<>();
list.add("b");
list.add("c");
list.add("a");
//会报查询索引的长度 < 数组的长度
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("a")) {
list.remove(i);
}
// #1
System.out.println(list.get(i));
}
foreach删除
foreach 删除导致的异常
会抛出 ConcurrentModificationException异常
erlang
for (String s : list){
if (s.equals("a")){
list.remove(s);
}
}
foreach是语法糖,是为了java程序员加速开发做的优化,因为虚拟机是不认识foreach语法的,经过javac编译之后会变成原本的样子 虚拟机才能执行。
vbnet
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
//#1 这里是关键点 这里会抛出 `ConcurrentModificationException`
String s = iterator.next();
if (s.equals("a")) {
//#2
list.remove(s);
}
}
Interator是List内部的类,它的next方法如下,他会调用checkForComodification() , 这个方法会在校验expectedModCount与modCount。
arduino
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
初始化 Iterator的时候(这里是内部类class Itr)modCount与expectedModCount一致都是更改次数。当调用list.remove()的时候
会调用 fastMove()方法。而这个方法是把modCount++ 增加了1。而下次再循环的时候还会执行 Iterator.next()方法就会导致 expectedModCount与modCount不相等抛异常了。
正确的方式
与上面的区别是 注意#1 位置,不要用list.remove() ,具体为啥上面已经说了。那为什么用iterator.remove就不会报错了了呢?
vbnet
//不会报异常
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next.equals("a")) {
//#1 这里不要用list.remove
iterator.remove();
}
}
iterator.remove()源码如下 注意 #2的位置,这里在删除之后会强行吧 expectedModCount = modCount这2个参数设置成一致,所以再调用.next()就不会抛出不一致的异常了。
csharp
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//调用父类的删除
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// #2
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
ConcurrentModificationException
是为了避免并发修改引发不一致的状态,这个异常表明在遍历集合时集合结构被修改了(如通过 remove
方法)。Java 的 Iterator
保证在遍历时结构不会被改变,因此需要使用 iterator.remove()
来安全地删除元素。