楼主的问题,看JDK源码就比较清楚了。下面是ArrayList的父类AbstractList的迭代器实现源码
- private class Itr implements Iterator<E> {
- /**
- * Index of element to be returned by subsequent call to next.
- */
- int cursor = 0;
- /**
- * Index of element returned by most recent call to next or
- * previous. Reset to -1 if this element is deleted by a call
- * to remove.
- */
- int lastRet = -1;
- /**
- * The modCount value that the iterator believes that the backing
- * List should have. If this expectation is violated, the iterator
- * has detected concurrent modification.
- */
- int expectedModCount = modCount;
- public boolean hasNext() {
- return cursor != size();
- }
- public E next() {
- checkForComodification();
- try {
- int i = cursor;
- E next = get(i);
- lastRet = i;
- cursor = i + 1;
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
复制代码
其实迭代器的两个主要方法hasNext()和next()的实现原理并不复杂,靠的就是一个cursor变量去指向List的每个元素。hasNext()中,只要cursor不等于List的元素个数,就表示还有元素可以取出。next()中就是返回cursor指向的List元素,并把cursor加1,指向下一个元素。如果我们在迭代器遍历过程中用List集合的方法改变了元素个数会如何呢?
比如一个List是[1, 2, 3, 4, 5],cursor当前值为2,即指向元素3。如果此时在尾部增加一个元素,这倒不影响迭代器读取,因为cursor是往前走的,尾部延长1,cursor多走1步就是了。但如果是在头部插入一个元素,比如变成[6, 1, 2, 3, 4, 5],那么此时下标为2的元素变成了2,如果调用next()方法,之前已经遍历过的元素2就会被重复输出。
如果是删除元素呢?还是[1, 2, 3, 4, 5],cursor当前值为2。如果删除元素5,显然也不影响迭代器读取。但如果删除了元素1,变成[2, 3, 4, 5],那么cursor值为2实际上指向的是元素4,元素3被略过了。如果考虑到多线程环境,还可能出现这样的情况:List集合的remove方法已经删除了元素1,并将2,3,4,5往左移动一个位置,但尚未将表示List元素个数的变量减1,线程就切换到迭代器,cursor值一旦变为4,就导致越界。因此next()方法的源码中,如果出现数组越界,就会调用checkForComodification()方法检查是不是底层List被修改了,如果是,就抛出并发修改异常。
那么使用迭代器提供的方法修改元素个数为什么不会出错呢?这可以看remove()方法的代码。remove中删除元素其实调用的还是List自身的remove方法,但是只允许删除cursor当前指向的元素,且删除后对cursor做了减1处理,保证迭代器和底层List相一致。 |