只记得在哪看到,说在集合对象的增强for时,不能进行增删改操作,不然就会报错。ps:不是所有的容器都会报错,有些是fail-safe
下面是我拷来的说明:
fail-fast:直接在容器上进行遍历,在遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常导致遍历失败。java.util包下的集合类都是快速失败机制的, 常见的的使用fail-fast方式遍历的容器有HashMap和ArrayList等。
在使用迭代器遍历一个集合对象时,比如增强for,如果遍历过程中对集合对象的内容进行了修改(增删改),会抛出ConcurrentModificationException 异常.
并没有去深度理解为什么,只知道不要这么做就好,然后我今天看到这么一段代码,执行不会报错。
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默是今晚的康桥");
for (String str : list) {
if ("沉默王二".equals(str)) {
list.remove(str);
}
}
System.out.println(list);
}
很难接受,但是不得不接受,所以决定看下到底遍历时报错是因为什么。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这是在遍历时进行增删改会报错的原因(ArrayList)。
modCount用于记录操作集合的次数
expectedModCount用于记录迭代开始之前的操作次数
所以在迭代之后,如果再继续修改集合,那就会出现modCount != expectedModCount,出现异常。上面就是为什么会报异常的原因。
为什么会进入进这个判断,我们可以反编译测试类的class,得到这个
public static void main(String[] args) {
ArrayList<String> list = new ArrayList();
list.add("沉默王二");
list.add("沉默是今晚的康桥");
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String str = (String)var2.next();
if ("沉默王二".equals(str)) {
list.remove(str);
}
}
System.out.println(list);
}
for-each循环的本质也是使用iterator迭代,不过不同的是,remove使用了list的remove方法,remove改变modCount,再次执行next方法时,执行checkForComodification方法报错
那为什么最初的那个示例不报异常呢?
因为迭代时的hasNext长这样
public boolean hasNext() {
return cursor != size;
}
cursor是现在迭代到哪一个了
size是集合大小
删除掉“沉默王二”之后,size大小是1,而cursor恰好也为1,以为自己迭代结束,然后跳出for了。自然就不会报错。虽然不会报错,但是结果不一定正确
例如
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王二");
for (String str : list) {
if ("沉默王二".equals(str)) {
list.remove(str);
}
}
System.out.println(list);
}
应该返回空集合,但是却返回“[沉默王二]”
只要size和cursor恰好一致,让迭代结束,就不会报错,所以一下例子也不会报错
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("沉默王四");
for (String str : list) {
if ("沉默王二".equals(str)) {
list.remove(str);
list.remove("沉默王三");
}
}
System.out.println(list);
}
至于为什么使用Iterator的remove方法不会报错,是因为
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
在执行remove时,修改了expectedModCount。再判断时,自然不会错。