-
例如使用
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(); } }
-










