记录一下为何在使用for each 循环时,不能在循环体内直接使用集合的remove方法的原因;
因为for each循环的底层原理时用iterator来实现的,所以在遍历元素的时实际操作使用的时iterator;
但我们在循环体内部使用集合自身的remove方法时,以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
}
可以看见源码里面有个modCount++,该属性时ArrayList自身属性 默认为0,代表修改的次数,每次删除modCount都会自增;
因为for each底层原理时iterator,所以我们再看下iterator中hasnext、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];
}
其中关注下 checkForComodification()方法 ,其源码:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
其中有个 expectedModCount 该参数时iterator中的属性 ,代表使用iterator修改集合元素的次数;
当集合属性modCount 于expectedModCount次数不相等,则会抛异常;
当我们再for each中直接使用集合的remove方法时 modCount增加了,但是iterator并没有增加导致抛异常;
如果我们使用iterator再for each循环体中使用remove 不会抛异常的原因可以通过iterator中remove源码可知:
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();
}
}
其中 ArrayList.this.remove(lastRet) 调用的就是集合本身的remove方法 :
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
可以看见modCount会自增,所以在调用iterator的remove方法时modCount等于expectedModCount;
综上,则是再for each循环体不能直接使用集合的remove方法但能用iterator的remove方法的原因。