Java中Iterator迭代器学习笔记(源码分析)

本文详细解析了Java中Iterator接口的工作原理及其实现细节,包括hasNext()、next()和remove()方法的具体作用和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Iterator迭代器


在遍历集合的时候,会经常使用Iterator迭代器,其原理实现也较为简单,可以将其与单链表的实现进行对比理解。在Java中内置的Iterator接口中,主要是针对三个方法,如下所示:

  1. boolean hasNext();
    
  2. E next();
    
  3. void remove();
    

其中hasNext判断后续是否还有元素可以进行迭代,也可以理解为后面还有没有元素,next函数用于获取当前元素并将指针后移一位,remove函数用于移除一个元素。

接下来就基于上述三种方法对Iterator迭代器进行学习,参考ArrayList中关于Iterator的源码进行分析。

成员变量

首先对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;

        Itr() {}
}

根据Java API中的描述,三个成员变量分别具有以下含义:

  1. cursor:用于记录当前迭代器遍历到的位置,通过该属性访问元素,初始值为0
  2. lastRet:该属性用于记录上一次使用next()方法时遍历所在的位置,在调用remove方法后该属性值变回-1,初始值为-1
  3. expectedModCount:modCount是ArrayList的属性,用于记录对集合进行操作的次数,包括add、remove等方法。expectedModCount的应用是为了实现一种”快速失败"机制,即在使用迭代器的过程中如果对
    原集合(如ArrayList)进行了修改,此时modCount的值将会发生变化,二者不再相同,此时将会抛出并发修改异常:ConcurrentModificationException

可以看出,对于Iterator来说,其中cursor和lastRet两个变量是最为重要的,其中cursor是用于指向元素位置的,从0开始,只会往前不会后退,在使用next()方法获取当前位置之后,cursor指针会自动往前加一,而lastRet也会更新为之前的cursor值。根据上述这些描述,在不考虑remove的情况下,遍历的情形应该是这样的。
在这里插入图片描述

hasNext()函数

通过查看ArrayList中的源码得知,hasNext()的源码如下所示:

public boolean hasNext() {
            return cursor != size;
        }

通过上述代码可以得知,判断方法也较为简单,其中size为当前集合的数据长度,将集合中的元素当作数组来考虑的话,那么对于一个长度为size的数组,有效范围就是0到size-1,由于cursor永远指向下一个元素的位置,故当cursor等于size的时候,说明之前访问的元素已经是最后一个元素了,那么此时就没有下一个元素了。

next()函数

通过查看ArrayList中的源码得知,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];
        }

首先还是进行异常判断,将cursor使用变量i进行保存,如果i越界了,就需要抛出异常,之后就是前面提到的,返回cursor当前指向的元素,然后将lastRet设置为cursor的值,最后将cursor的值加一,也就是指向下一个元素。上述代码执行过程和原理解释部分顺序不太相同,但是实质是一样的。

remove()函数

通过查看ArrayList中的源码得知,remove()的源码如下所示:

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();
            }
        }

通过查看源码得知,程序一开始就会判断lastRet的合法性,由于lastRet初始值为-1,从这里也可以看出,至少要执行一次next()函数才能执行remove()函数,否则会抛出异常。

在判断了lastRet的合法性之后,接下来就是直接删除lastRet指向的元素,这个元素也就是前面刚刚访问过的元素。删除之后由于各个元素的位置会发生变化,所以cursor需要后退一格,这里使用cursor = lastRet来让cursor回退。同时将lastRet置为-1,表示刚刚进行了删除操作。

总结

Iterator实现类中的一些方法在理解时可能会很绕,不妨换一种角度,lastRet指向刚刚访问过的元素,初始化lastRet为-1,即代表不指向任何元素,next指向下一个要访问的元素,初始化为0,表示第一个元素。

hastNext()则是判断是否还有后续元素。

next()则是返回下一个元素,同时更新lastRet和cursor,例如当通过next()函数返回了对象“abc”之后,lastRet就指向对象“abc”,next()则是指向元素“abc”下一个元素。

remove()则是删除当前元素,即remove()删除的只是对于刚刚访问过的元素,也就是lastRet指向的元素进行操作,而不是对于cursor指向的元素进行操作,删除之后直接将lastRet置为-1。这也就代表了每一次remove()之前都需要调用至少一次next()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值