错误示例代码
@Test
public void testInteger() {
ArrayList list = new ArrayList(12);
list.add(111);
list.add(222);
list.add(333);
//获取迭代器
Iterator iterator = list.iterator();
//添加或者删除元素
list.remove(0);
//开始迭代
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
原因分析
1.ArrayList继承了AbstractArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
2.ArrayList中有一个modCount变量
3.modCount的作用是记录ArrayList集合中的元素修改(包含添加和移除)
以添加为例
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//此处修改值
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
由上面这段代码的第三个方法可知,当使用add方法的时候modCount已经发生改变
4.当我们使用iterator()方法的时候,返回的是一个实现了Iterable接口的Itr内部类
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
5.由以上代码可知,当获取Itr对象的时候,会对该内部类中的expectedModCount赋值,且值和modCount相等
6.当使用next来获取迭代器中的元素时,有一个checkForComodification()方法会对expectedModCount和modeCount值进行校验
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
7.所以当ArrayList中元素的个数发生变化的时候,modCount值就会发生变化,而expectedModCount的值却还是之前的modCount的值,此时如果两者不相等,就会抛出标题所示异常