ListIterator迭代器以及cursor,lastRet游标

前言

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator 。 Iterator 接口也是Java集合中的一员,但它与 Collection 、 Map 接口有所不同, Collection 接口与 Map 接口主要用于存储元素,而 Iterator 主要用于迭代访问(即遍历)。Collection 中的元素,因此 Iterator 对象也被称为迭代器。

迭代器

迭代的定义:

即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素(hashNext()),如果存在,则调用next()加粗样式方法将元素取出,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
Iterator用来迭代Collection下的所有的集合 ListIterator是Iterator的的子类 ListIterator只能迭代List的集合

常用方法

在这里插入图片描述

具体实例讲解迭代器实现的过程

public static void main(String[] args) {
    ArrayList<Integer> date = new ArrayList<>();
    date.add(1);
    date.add(2);
    date.add(3);
    ListIterator<Integer> iter = date.listIterator();
    int a = 0 ;
    a = 100;
    iter.add(100);
    System.out.println(date);
    iter.next();     
    iter.next();   
    iter.set(200); 
    System.out.println(date);

    iter.previous();
    iter.previous();
    iter.set(300);
    System.out.println(date);
}

(一)

以上的的代码执行刚开始可能是这么理解的:
集合date依次添加三个元素1 2 3,然后再迭代器iter在最前面加上元素100,此时下标指向0,此时集合里的值为[100, 1, 2, 3]。
然后将下标向下走2步移到下标为2,也就是值为2的元素,通过set方法将2变为200,然后通过previous方法将下标再往前走两步,回到下标为0的地方,
让通过set方法将100变为300,所以很多人认为输出为[300,1,200,3]。但是这是错误的答案。
正确的答案应该为:[100,300,200,3]

(二)

具体为什么输出是这样的呢,我们debug一下代码并结合各方法的源码一步一步地解读一下。

首先我们先引入两个游标的概念:cursor和lastRet
cursor:表示下一个元素的索引位置
lastRet:表示上一个元素的索引位置

当我们执行完第6行之后,可以看到cursor和lastRet的初始值为0和-1

此时iter指向的是下标为0的前面,之后进行iter.add(100),我们看回add方法的源码:

public void add(E e) {
    checkForComodification();

    try {
        int i = cursor;
        ArrayList.this.add(i, e);
        cursor = i + 1;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

上面已经说了此时的cursor为0,我们看到第6行代码即在集合中下标为0的地方添加一个值为100的元素,那么其他元素顺位往下一个,此时输出集合为:[100, 1, 2, 3]
在上图源码中我们还看到cursor加了1变成了1,lastRet仍为-1

(三)

当我们执行到第一个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加了1变为了2,lastRet = cursor原先的值变为1
接着执行完第二个next()方法之后,cursor=3,lastRet=2

(四)

然后我们执行iter.set(200);将下标为2的值替换成200,我们此时输出集合为:[100, 1, 200, 3]
在这之前我先跟大家说一声,表示iter现在所指的下标是多少就看lastRet。不信我们点开set()方法的源码:

public void set(E e) {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.set(lastRet, e);
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

主要看第7行代码,代表的就是在lastRet下标的位置用e将集合原先的值替换掉。
我们还可以试着在在最上面的代码中,在执行第一个iter.next()之前执行iter.set(“900”),如果你尝试这样写会发现运行出现异常。因为此时的lastRet值还为-1.
到这里可能有同学想问,既然lastRet =-1,那为什么运行iter.add(100),不会出错呢?那么你看回上面next方法的源码就知道为什么了,因为执行next方法的时候用到的是curser,并不是lastRet。

(五)

我们继续回到主程序,执行完iter.set(200)之后接着是iter.previous 还是先看源码:

public E previous() {
    checkForComodification();
    int i = cursor - 1;
    if (i < 0)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i;
    return (E) elementData[lastRet = i];
}

可见cursor=cursor-1 lastRet=原先的cursor
执行完第一个previous,cursor=2,lastRet=2
执行完第二个previous方法,cursor=1 ,lastRet = 1

最后将执行set方法,将集合中下标为lastRet的值退换成300.
最终我们输出的结果为:[100, 300, 200, 3]

整个程序执行的结果为
在这里插入图片描述

以上是基于一般情况进行讲解,因为我们看到源码上还有很多边界或关于范围的情况可能会抛出异常,如感兴趣,可根据源码,进一步研究并总结。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值