ConcurrentModificationException异常的出现时机

1.Iterator迭代,原始结构发生改变; 举例如下

ArrayList<String> arrayList = new  ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator<String> iterator =  arrayList.iterator(); // expectedModCount = modCount = 3  ;
while(iterator.hasNext()) {
    String next = iterator.next();    //  modCount=3;>>modCount != expectedModCount
    arrayList.remove(next);           //  modCount=4;>>
}

当执行arrayList.iterator()时,首先如下生成一个iterator

public Iterator<E> iterator() {
    return new Itr();
}

Itr源码如下:类在初始化时,会记录expectedModCount的值为modCount的值,modCount是在AbstractList中定义的,ArrayList初始化时,其值为0,在arrayList执行add(E)方法时,modCount自增。所以在执行arrayList.iterator()语句之后,expectedModCount = modCount = 3;

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];
}
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();
	}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E>  consumer) {
	Objects.requireNonNull(consumer);
	final int size = ArrayList.this.size;
	int i = cursor;
	if (i >= size) {
		return;
	}
	final Object[] elementData =  ArrayList.this.elementData;
	if (i >= elementData.length) {
		throw new  ConcurrentModificationException();
	}
	while (i != size && modCount ==  expectedModCount) {
		consumer.accept((E) elementData[i++]);
	}
	// update once at end of iteration to reduce  heap write traffic
	cursor = i;
	lastRet = i - 1;
	checkForComodification();
}
final void checkForComodification() {
	if (modCount != expectedModCount)
		throw new  ConcurrentModificationException();
}

如下实例代码,首次执行iterator.next()时,modCount=3,所以在checkForComodificaion方法校验时,并没有出现异常,第二次执行该语句时,因为之前的arrayList.remove(next);语句执行过程中,修改了modCount的值,所以,modCount != expectedModCount成立,导致抛出异常。

while(iterator.hasNext()) {
    String next = iterator.next();    //  modCount=3;>> modCount != expectedModCount
    arrayList.remove(next);           //  modCount=4;>>
}
  另: arrayList.remove(next);源码如下:
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;
    }

arrayList是可以移除Null元素的。并且,每次移除时,都需要执行fastRemove(index);方法,该方法源码如下:

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
    }
注意fastRemove中,完成了modCount自增操作。
*快速移除,使用了System.arrayCopy方法:

elementData[--size] =null ; 复制完成后,将最后一个元素置null,便于GC收集。


2. for循环迭代,元素个数!=2的情况下

ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
for(String s:arrayList) {
     arrayList.remove(s);
}
抛出异常的主要原因是:编译器在解释加强型for循环时,自动解释为iterator迭代实现(见第二种情况)。
为什么size=2时,并没有抛出异常?源码如下示例
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");// modCount = 2;
for(String s:arrayList) {   //iterator()-->next()--> excpetionModCount=2  ;iterator()-->next()-->excpetionModCount!=modCount
  arrayList.remove(s);   //modCount = 3;
}
arrayList.remove(E);源码如(Iterator迭代方式中展示),注意fastRemove()方法的源码:
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,则将最后一个元素置为null,那么当程序再次进入for循环中时,执行iterator.hasNext()方法,源码如下

public boolean hasNext() {
    return cursor != size;
}
cursor的值与size相等,所以,就不会再执行iterator.next()方法了。但是,cursor与size怎么会相等?
看iterator的第一次执行next方法时,源码如下:
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];
}

cursor完成自增操作,所以第二次进入hasNext()方法时,cursor = 1, 而size值由arrayList.remove(E)中自减,最后,导致cursor = size;所以,没有抛出异常,而是正常退出程序。

看一下当size=1时,为什么会抛出异常?
因为当进入for循环时,第一次执行完hasNext以及next方法后,cursor=1,而size值由arrayList.remove(E)中自减(size=0),这个时候,再次进入循环时,由于hasNext()方法是通过判断cursor与size的值确认是否有下一个元素,正常情况下,cursor<=size,但是此处是特殊情况,cursor>size,所以通过下一个元素确认,执行next()方法时,checkForComodification()方法抛出异常。

猜你喜欢

转载自blog.csdn.net/jianlong1284537512/article/details/79365893
今日推荐