-
例如使用
Arraylist
集合返回的一个iterator迭代器,我们
这样使用会报错
:-
ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); Iterator<Integer> iterator = arrayList.iterator();//获得迭代器 arrayList.add(2); if (iterator.hasNext()) System.out.println(iterator.next());
-
-
分析
:-
我们来看**
iterator.next()
方法源码**: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]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
-
ArrayList
继承了
AbstractList
,
其中**
AbstractList
** 中有个**
modCount
** 代表了
集合修改的次数
。在**
ArrayList
的
iterator
的
next()
方法中会判断**
expectedModCount
与
modCount
是否相等,如果相等继续执行,不相等报错
。 -
这个
expectedModCount
是迭代器这个类的一个属性,
调用
ArrayList
自身的
add
和“remove
等改变集合大小的方法都会导致
modCount+1
,但是不会修改
expectedModCount`
。 -
返回的
迭代器是
ArrayList
的一个私有内部类(下文有这个内部类的源码)
,在调用集合的iterator()方法返回迭代器的时候底层是new一个新的迭代器返回,它在
创建的时候会将当前
modCount
的值赋给
expectedModCount
,因此创建的时候两者相等
。 -
因此我们在创建迭代器后,如果使用了
ArrayList
本身的add方法导致
modCount+1
后,此时
modCount
和
expectedModCount
不相等了,再调用迭代器的next()方法就会报错
。-
注意:这里不止调用
add
会报错,调用
ArrayList
本身的
remove
也会报错,
关键在于这些方法修改了
modCount
但是没有修改
expectedModCount
。 -
如果我们是用迭代器的
remove
方法则不会报错,因为迭代器的remove方法在删除元素后会将新的
modCount
的值赋给
expectedModCount
:public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet;//这个cursor表示的是下一个需要遍历的元素,lastRet表示上一次遍历到的元素 lastRet = -1; expectedModCount = modCount;//这里会保持两者是一致的 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
-
注意:我们在删除
lastRet
索引位置表示的上一个遍历的元素后,将
lastRet
的索引赋给
cursor
表示下一个需要遍历的索引位置
。-
这个时候可能会有疑惑:为什么这个位置元素被删除了下一次遍历还是这个位置
?-
这是因为我们
remove
删除上一个元素后,数组大小-1,即被删除元素后的元素都会往前移动,此时lastRet这个索引在新数组中指向的就是刚刚的下一个元素
。
-
-
-
删除之后将
lastRet
置为-1,而remove方法开始就会判断
lastRet
是否小于0,防止连续两次调用
remove
方法删除元素
。
-
-
-
ArrayList中的iterator私有内部类是这样定义的:
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(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
-