class="java">package com.ljn.base;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* “对ArrayList进行遍历时,不要进行list.remove或者list.add操作”
* 我只是记住了这条规则,但规则的背后,是什么原理?
* 通过代码来说明:
*/
public class ConcurrentModificationExceptionTest {
public static void main(String[] args) {
remove(newArrayList(), "b");
remove(newArrayList(), "a");
}
private static void remove(List<String> list, String itemToRemove) {
Iterator<String> iterator = list.iterator();
int i = 1;
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(i++);
if (item.equals(itemToRemove)) {
list.remove(item);
System.out.println(item + " is removed");
} else {
System.out.println(item);
}
}
System.out.println();
}
private static List<String> newArrayList() {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
return list;
}
/*
很简单的程序,输出如下:
1
a
2
b is removed
1
a is removed
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
为什么remove(newArrayList(), "a")会抛异常,而remove(newArrayList(), "b")就不会?
AbstractList里面有一个字段modCount,用来记录List“结构性改变”的次数,执行add或者remove都会使得modCount加1
AbstractList.iterator会创建一个AbstractList.Itr(内部类),而Itr备份了modCount,命名为expectedModCount
Itr执行next和remove操作前,都会调用checkForComodification方法检查expectedModCount是否等于modCount,如果不等,
就报ConcurrentModificationException
同时注意到Itr有一个指针cursor,表示下一个要遍历的元素的下标,初始值为0
Itr调用hasNext时,会将cursor与数组大小比较:
public boolean hasNext() {
return cursor != size();
}
回到例子
先看remove(newArrayList(), "a"):
1.newArrayList()之后,modCount=3
2.list.iterator()之后,Itr的expectedModCount=modCount=3
3.调用hasNext(),next(),再调用list.remove(item),此时modCount=4
4.调用hasNext(),next(),在next里面进行检查,发现expectedModCount不等于modCount,于是报错
再看remove(newArrayList(), "b"):
1.newArrayList()之后,modCount=3, list.size=3
2.list.iterator()之后,Itr的expectedModCount=modCount=3,cursor=0
3.调用hasNext(),next(),打印a,list.size=2,cursor=1;没有对list做修改,因此expectedModCount=3
4.调用hasNext(),next(),检查expectedModCount和modCount,都等于3,因此检查通过,b is removed;
此时cursor=2,size=2
5.调用hasNext,此时cursor 与 size()都等于2,hasNext返回false,循环结束
由此可见,remove(newArrayList(), "b")只是侥幸地成功运行了,但仍然是错误的用法
*/
}