public class Test {
public static void main(String[] args) throws Exception{
List<String> strs = new ArrayList<String>();
strs.add("zhangsan");
strs.add("lisi");
strs.add("wangwu");
strs.add("zhaoliu");
strs.add("chenqi");
Iterator<String> it = strs.iterator();
while(it.hasNext()) {
String str = it.next();
if(str.equals("wangwu")) {
strs.remove(str);
}
}
}
}
这段代码运行出错:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at com.xtayfjpk.Test.main(Test.java:18)即String str = it.next();这句代码出错
这是因为ArrayList从AbstracArrayList继承而来,而在AbstracArrayList中有一个modCount字段,该字段表示这个List被structurally modifie的次数,ArrayList中的add,addAll,remove,clear,removeRange方法都会使modCount加1,当使用iterator方法得到Iterator对象(迭代器),其实是返回了一个Iterator接口的实现类AbstractList$Itr,该类为AbstracArrayList的一内部类,AbstractList$Itr类中有一个expectedModCount字段,当调用AbstractList$Itr的next方法时会先检查modCount的值是否等于expectedModCount的值,当两者值不相等时就会抛出java.util.ConcurrentModificationException异常。
为什么会抛出该异常呢?从代码可以看到调用了5次add方法,这时modCount的值也就为5,当当使用iterator方法得到Iterator对象时把modCount的值赋给了expectedModCount,开始时expectedModCount与modCount是相等的,当迭代到第三个元素(index=2)zhangsan”时,因为if条件为true,于是又调用了remove方法,调用remove方法时modCount值又加1,此时modCount的值为6了,而expectedModCount的值并没有改变,当再次调用AbstractList$Itr的next方法时检测到modeCount与expectedModCount不相等了,于是抛出异常。
当又发现当把if语句写成if(str.equals("zhaoliu"))时又没有抛出该异常,这又是为什么呢?一开始觉得很奇怪,这不是一样的吗,只不过换了一个元素而已,但后来仔细看才明白过来。AbstractList$Itr中还有一个名为cursor的字段用来指向迭代时要操作的元素索引,初始值为0,每调用一次next方法该字段值加1,注意是先从集合中取出了元素再加1的。当判断"zhaoliu"时,注意是倒数第二个元素,这些cursor的值为4,移除掉元素"zhaoliu"时,List的size为4,当调用AbstractList$Itr的hasNext方法判断有无下一个元素时,判断的依据为cursor的值与size是否相等,不相等则还有下一个元素,现在大家发现了,此时两者值刚好相等,也就没有往下执行next方法了,也就没有抛出异常了,这就是为什么说移动倒数第二个元素时不会抛异常的异常的原因了。
结论:当对ArrayList进行迭代的过程中不允许对ArrayList中的元素进行添加移除操作。
如果大家有这种需求一是可以使用java.util.concurrent.CopyOnWriteArrayList替代,该类允许在迭代的过程中对其中的元素进行添加移除操作,并且是线程安全的。二是迭代时返回ListIterator对象,ListIterator类中提供了remove和set方法用来操作next方法或previous方法返回的元素。