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