-
例如使用
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(); } }
-
在java集合中调用remove方法后调用iterator.next()报错
最新推荐文章于 2022-08-05 20:35:13 发布