在阿里巴巴《码出高效》中存在这么一条,下面代码的执行结果可能和想象的不一样,运行结果如下:
// 当if判断条件中为“1”时,代码能够正常执行,不会抛出任何异常
// 当if判断条件为"2"时,会抛出下面的异常,通过以上信息,我们发现是ArrayList$Itr.next方法抛出的异常
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.zkq.ArrayListForeach.main(ArrayListForeach.java:22)
通过增强for进行循环操作就相等于使用Iterator进行迭代操作,那么这个Iterator是ArrayList类中存在一个内部类Itr,下面看一下这个内部类:
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
String item= iterator.next();
System.out.println(item);
if("2".equals(item)){
list.remove(item);
}
}
ArrayList类中存在一个内部类Itr,看一下这个类的主要代码
private class Itr implements Iterator<E> {
// 表示下一个元素的位置
int cursor;
int lastRet = -1; // index of last element returned; -1 if no such
// expectedModCount 表示期望修改的次数,modCount表示实际修改的次数,这里是Java中的fast-fail机制,不了解的先了解一下fast-fail
int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
// 这个checkForComodification就是用于检查expectedModCount 与modCount是否相等,不相等时就会抛出ConcurrentModificationException
checkForComodification();
}
// 判断是否还有下一个元素
public boolean hasNext() {
return cursor != size;
}
}
下面ArrayList进行源代码的分析
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
// ArrayList中删除时调用的方法
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 这里只是根据元素值得到要删除元素的索引,然后调用真正的删除方法
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
// 真正删除的方法
private void fastRemove(int index) {
// 注意这个值,这是关键,稍后会进行分析
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
}
elementData[–size] = null;这行代码就是为什么删除"1"时不会报异常而删除"2"时会报异常的原因
我们假设ArrayList存在两个元素即elementData.length==2进行下面的分析
- 进行删除操作时对表示ArrayList修改次数的modCount+1,这里+1后modCount与expectedModCount 不再相等,但是只有在迭代器的next()方法中才会调用checkForComodification()方法判断这两个值是否相等,但是删除"1"时,执行elementData[–size] 这行代码后,size就变成了1,Itr类中cursor表示下一个元素的位置,cursor的值也为1,所以通过hasNext()方法判断时返回false,next()方法没有机会执行,不会抛出异常
- 删除"2"时,elementData[–size] 这行代码后,size就变成了1,Itr类中cursor表示下一个元素的位置,cursor的值为2,所以通过hasNext()方法判断时返回true,next()方法执行判断expectedModCount 与modCount不相等,就会抛出ConcurrentModificationException