先来看一段代码,摘自阿里巴巴java开发规范:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item: list) {
if ("1".equals(item)) {
list.remove(item);
}
}
可以发现是可以执行的,但事实上循环只执行了一次便停止了(原因在下文讲述),但如果要删除"2",便会报如下错误:
这个错误是指存在并发修改,通常在多线程下会出现,但单线程也会出现,本文的错误是因为***迭代器和list集合在同时操作集合内的内容,发生冲突***。
foreach是增强版的for,代码简洁,其底层实现是通过迭代器实现的。为了分析代码,将class文件反编译如下:
List list = new ArrayList();
list.add("1");
list.add("2");
Iterator i$ = a.iterator();
do
{
if(!i$.hasNext())
break;
String temp = (String)i$.next();
if("1".equals(temp))
a.remove(temp);
} while (true);
hasNext()、next()、remove()方法是引起问题的关键,并且需要注意的是这里的remove()方法还是集合list的方法。
先看一下list.remove()方法的核心方法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
}
可以看到在删除集合元素的同时,modCount自增了,先记住这个参数就好,下文会提。
作为对比,来看下iterator()方法:
public Iterator<E> iterator() {
return new Itr();
}
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;
}
@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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
可以看到迭代器中的remove方法让expectedModCount与modCount保证相等,这便是与list.remove()方法的区别。
下面需要几点:
1.在iterator初始化的时候,expectedModCount == modCount,并且等于集合容量大小。
2.报ConcurrentModificationException异常是因为expectedModCount != modCount。
可以分析文初的案例:
case1:执行完一次循环后,modCount = 3,expectedModCount = 2,lastRet = 0,cursor == size == 1,所以只执行了一次循环,并未报错。
case2:执行完两次循环后,modCount = 3,expectedModCount = 2,lastRet = 1,cursor = 2,size = 1,继续进入第三次循环,进入next()方法的checkForComodification()方法中,由于modCount和expectedModCount不想等,报异常!
分析到这,便有了答案,集合的add方法也是如此,这里便不赘述!