我们的错误代码是这样
List<String> list = new ArrayList();
for (String o : list) {
if ("".equals(o)){
list.remove(o);
}
}
首先我们看一下ArrayList中的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;
}
//我们在循环中经常会用到这个方法进行删除
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
}
我们可以看到fastRemove中第一行进行了modeCount++,其他的我们可以先忽略,接下来我们看一下ArrayList中的迭代器私有类
private class Itr implements Iterator<E> {
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;
}
final void checkForComodification() {
//每次比较两个参数的值是否相同,modeCount是ArrayList的属性,上面的remove
//中提到会进行++操作但是外层的remove并没有进行expectedModCount的维护
//可以向下看itr中自己的remove操作对比一下
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
@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 {
//这一步同样是操作外层的remove(int x)这个方法
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//可以看到itr中的remove会多执行这一步
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
//没有用到暂时不看啦
}
}
看过上面的代码我们可以发现只有使用迭代器并且使用迭代器的remove才能保证没有问题,如果使用迭代器循环但是使用了外层的remove还是会有问题