Java Iterator原理分析

jojo
发布于 2020-9-16 13:48
浏览
0收藏

所有Iterator都最终实现接口Iterator,Iterator接口中包含三个基本方法,next(), hasNext(), remove(),其中对于List的遍历删除只能用Iterator的remove方法;JDK1.8中Iterator接口的源码如下:

public interface Iterator<E> {

    boolean hasNext();

    // JDK1.8的新特性,可以通过default在接口中写个方法的实现
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 

下面将基于ArrayList的Iterator的实现分析Iterator的原理(基于JDK1.8):

 

1.在ArrayList类中有个方法iterator(),此方法将返回一个iterator的实现,这里可以看出实现类叫Itr,通过其它源码可知,此类是AarryList的内部类,即ArryList的Iterator实现在ArrayList内部;

    public Iterator<E> iterator() {
        return new Itr();
    }
  • 1.
  • 2.
  • 3.

 

2.下面重点看下ArrayList中实现类Itr类的源码:

private class Itr implements Iterator<E> {
        /**
         * 下一个返回的位置
         */
        int cursor = 0;

        /**
         * 当前操作的位置
         */
        int lastRet = -1;

        /**
         * 类似版本号,检查List是否有更新
         */
        int expectedModCount = modCount;

        public boolean hasNext() {      // 判断是否有下一个元素
            return cursor != size();
        }

        public E next() {  // 返回下一个元素
            checkForComodification();
            try {
                int i = cursor;     // cursor记录的是下一个元素,所以调用next时将返回的是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();     //   检查是否有更改,remove或者add

            try {
                AbstractList.this.remove(lastRet);   // 删除当前元素  
                if (lastRet < cursor)                // 删除了之后指标减1
                    cursor--;
                lastRet = -1;                    
                expectedModCount = modCount;         // 保持版本号一致
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {    // 如果有更改则抛出ConcurrentModificationException异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

 

从上面代码中可以看出,对于Iterator的实现中主要有几个变量cursor,lastRest, expectedModCount三个变量,其中cursor将记录下一个位置,lastRet记录当前位置,expectedModCount记录没有修改的List的版本号。

 

问题:还记得说List中在iterator遍历的时候,不能随便添加和删除元素吗,这是为什么呢?

 

在iterator遍历的时候抛出异常都是checkForComodification作的,根本原因是modCout和expectedModCount不相等,导致抛出异常

 

那为啥会不相等呢?

 

可以看看ArrayList的add和remove方法,

 

remove方法:

Java Iterator原理分析-鸿蒙开发者社区

 

add方法:

Java Iterator原理分析-鸿蒙开发者社区

 

从上面的代码中可以看出只要对ArrayList作了添加或删除操作都会增加modCount版本号,这样的意思是在迭代期间,会不断检查modCount和迭代器持有的expectedModCount两者是不是相等,如果不想的就抛出异常了。

 

这样在迭代器迭代期间不能对ArrayList作任何增删操作,但是可以通过iterator的remove作删除操作,从之前的代码可以看出,在iterator的remove()中有一行代码,expectedModCount = modCount; 这个赋值操作保证了iterator的remove是可用性的。

 

当然,iterator期间不能增删的根本原因是ArrayList遍历会不准,就像遍历数组的时候改变了数组的长度一样

 

 

作者:0li0

来源:CSDN

分类
收藏
回复
举报


回复
    相关推荐