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()方法抛出异常。