public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
// list.remove("C");
System.out.println(iterator.next());
}
}
以上代码运行都没有问题,就是取出List数据,然后打印。
但是要是把注释打开那就会报错,java.util.ConcurrentModificationException,这里是为什么呢?那就来讨论一下迭代器的原理吧。

通过list.iterator查找发现上面截图,里面创建了Itr对象,下面就看看这个对象的具体内容。
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
Itr是AbstractList的一个内部类。
方法hasNext:比对cursor(下一个要访问的元素的索引)与当前长度。
方法next:根据cursor值获取元素信息。
方法remove:删除lastRet(上一个访问元素的索引)值获取元素信息。
方法checkForComodification:检验(expectedModCount与modCount是否相等),前面的错误就是这里产生的。
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
}
ArrayList的方法remove只是修改了参数modCount。
也就是在使用迭代器遍历的时候,就不能使用List自身的方法(add/remove)去修改原来的List的结构,要不会报错,而使用迭代器自身的方法(remove)是不会报错的,这又是为什么呢?

上图中的红框中明显发现,是把下一个访问的元素下标及上一个访问元素下标给修改了。
总结
JAVA的迭代器更像是数据库中的一个镜像,当你使用原始List修改了原始数据结构时,它会报错,必须使用迭代器的方法remove才可以不会报错。
原文:http://my.oschina.net/u/1269959/blog/529602