这里以ArrayList为例介绍List实现类在循环过程删除元素会遇到的问题。
一个ArrayList在循环过程中删除,会不会出问题,为什么?接下来给大家详细解释一下在这个过程中会出现的问题和原因及解决方法。
ArrayList中的remove方法(注意ArrayList中的remove有两个同名方法,只是输入参数不同,这里看的是输入参数是Object的remove方法)是怎么实现的:
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; // Let gc do its work
}
执行System.arraycopy方法,导致删除元素时涉及到数组元素的移动。
for each写法是对实际的Iterator、hasNext、next方法的简写,问题出在上文的fastRemove中,可以看到第一行把modCount变量的值加1,但在ArrayList返回的迭代器(该代码在其父类AbstractList中)。
public Iterator<E> iterator() {
return new Itr();
}
这里返回的是AbstractList类内部的迭代器实现private class Itr implements Iterator,看这个类的next方法。
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这里会做迭代器内部修改次数检查,因为你上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。要避免这种情况的出现,则在使用迭代器迭代时(显式或for each的隐式)不要使用ArrayList的remove,改用Iterator的remove即可。
总结:错误原因都是ArrayList集合中remove方法底层的源码中有一个fastRemove(index)方法,然后会有一个modCount++的操作,然后在ArratList内部的迭代器中有一个checkForComodification操作,也就是检查modCount是否改变,如果改变了,就抛出并发修改错误。同样的在For each增强for循环中,也是利用了ArrayList自身的Iterator迭代器,也是会出现这样的错误。
注意,但是,通过Iterator的remove方法又会遇到另一个问题,具体
List 循环遍历中删除元素问题二
。