public static void main(String[] args) { Listlist = new ArrayList (); list.add("A"); list.add("B"); list.add("C"); Iterator 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{ 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才可以不会报错。