深入刨析ArrayList和LinkedList迭代器差异

一、引言

大多数列表迭代器相关文章和教学视频对于迭代器浅尝辄止,使得我们继续学习集合框架源码过程中频频出现问题。我写这篇文章初衷,就是因为我在学习迭代器时我发现初始化一个迭代器时,此时迭代器是指向第一个元素还是第一个元素前面呢,因此我带着好奇心去研究源码才明白。那么接下来让我们深入理解不同list实现类的迭代器

Alt

二、快速了解ArrayList和LinkedList

ArrayList

  • 是一个实现了List接口的类,它提供了类似动态数组的数据结构
  • 它允许您存储和操作一组元素
  • 在添加或删除元素时动态调整大小,不同于普通数组具有固定大小的特性

LinkedList

  • 是一种实现了List接口的类,它使用链表数据结构来存储和操作元素
  • ArrayList不同,LinkedList在插入和删除元素时的性能较好,因为它不需要像ArrayList那样进行数组的重新分配

三、俩者iterator()异同

为了提高大家阅读效果,在具体源码讲解之前先将结果展示在下文,接下来我会针对这三个方法进行源码分析。

ArrayList 迭代器常用方法

Iterator<E> iterator() // 返回迭代器对象,默认指向当前集合的0
索引
boolean hashNext() // 判断下一个位置是否有元素,有返回true,无返回false
E next() // 获取当前位置的元素,并将迭代器对象移向下一个位置

LinkedList 迭代器常用方法

Iterator<E> iterator() // 返回迭代器对象,默认指向当前集合的0
索引的前面
boolean hashNext() // 判断下一个位置是否有元素,有返回true,无返回false
E next() // 指针先下移,并获取此时指针所指元素值
3.1 示例代码
    List<Integer> list = new ArrayList<>();
    // List<Integer> list = new LinkedList<>();

    list.add(1);
    list.add(2);
    list.add(3);
// 获取迭代器对象
    Iterator<Integer> coll = list.iterator();
    while (coll.hasNext()){ // 判断是否含有下一个元素
        int b = coll.next();
        System.out.println(b);
    }    
3.1 Iterator iterator()

该方法作用是获取迭代器对象,因此在这里主要解决的俩个问题是,初始化迭代器时,迭代器此时指向哪个元素?

image-20230824160010362

ArrayList 获取迭代器对象是通过调用ArrayList类中iterator方法。

image-20230824155920800

进入Itr类(该类属于LinkedList类私有类)具体实现过程中,我们可以发现三个变量,以及相关迭代器的方法。其中cursor表示游标意思,也就是类似于索引的一种概念,LastRet表示最后索引,modCount表示该集合修改次数,用于判断迭代过程中集合是否被修改。

image-20230824160541866

继续debugger我们可以发现红框中出现上面所讲的三个变量,此时我们发现初始化迭代器后,cursor=0,而在红框下面的this$0中0代表元素值1。因此,当我们采用ArrayList时,其迭代器对象初始化时所指为迭代器第一个元素。

image-20230824161617251

同样LinkedList 获取迭代器对象也是通过调用ArrayList类中iterator方法。

image-20230824162009924

那么我们继续进入ListItr类(该类属于LinkedList类私有类),继续debug我们可以发现ListItr类内变量和一些相关方法。其中next用于记录下一个节点,nextindex用于记录是否含有下一个元素, lastReturned表示最后返回值,也就是next所取出的值。

image-20230824162937363

第一个红框是构造方法,其中,下面该代码,我们可以发现当index不等于集合长度时,next指向迭代器第一个节点。

next = (index == size) ? null : node(index);

image-20230824164747272

如上图所示,可以发现lastReturned=null,next此时指向节点值等于1的节点。因此LinkedList的迭代器对象初始化时,指向第一个节点前面。

总结

image-20230824165433140

ArrayList迭代器初始化时,其迭代器对象初始化时所指为迭代器第一个元素。

image-20230824165513228

LinkedList迭代器初始化时,指向第一个节点前面,也就是1前面。

3.2 boolean hashNext()

hashNext方法作用是判断下一个位置是否还有元素。

image-20230824170554307

ArrayList中的私有类Itr继承Iterator接口并重写接口方法,hashNext()实现方法如下:

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

size表示集合的长度,cursor表示游标,而ArrayList 迭代器对象通过cursor是否等于size判断下一个位置是否含有元素,也就是当cursor=size返回false,以本文代码举个例子,当cursor=3时,但迭代器中仅含有3个元素,因此使用cursor=3取值会越界。
image-20230824183154729

LinkedList中私有类ListItr继承ListIterator接口并重写相关方法

public boolean hasNext() {
    return nextIndex < size;
}

上述代码中nextIndex用于记录指针移动步数是否超过集合长度,初始值为0,size为集合长度。因此当nextIndex小于size时返回true,即判断下一个位置还有元素。

3.3 E next()
// ArrayList中私有类Itr的方法
public E next() {
    checkForComodification(); // 检查集合中元素长度是否发生变化
    int i = cursor; // 将游标值赋值给i
    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)的时候,如果在迭代的过程中有其他线程对集合进行了结构性修改(例如添加、删除元素),就会触发 ConcurrentModificationException 异常。若使用集合方法进行增删也会触发,例如在遍历循环中使用 list.remove(index)方法
//这个方法会检查一个内部计数器,该计数器记录了集合被修改的次数。如果在迭代的过程中发现计数器的值与预期不符,就会抛出 ConcurrentModificationException 异常,以防止出现并发修改问题。

为了让大家更理解,我将上述代码进行注释了。通过上面代码我们可以发现,ArrayList的迭代器对象中next方法通过游标值移动来获取迭代器中元素值。

public E next() {
    checkForComodification(); 
    if (!hasNext()) // 判断下一个位置是否为null,若为null,包无此元素异常
        throw new NoSuchElementException();

    lastReturned = next; // 赋值
    next = next.next; // next移动至下一个位置
    nextIndex++; // 记录指针移动次数,用于判断下一个位置是否含有元素
    return lastReturned.item;//返回next移动前位置的元素值
}
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

通过上述代码,我们可以发现LinkedList的迭代器对象中next方法是通过指针移动来获取元素值。此外,lastReturned用于记录next位置,next用于指向下一个位置,从而配合完成对迭代器中元素取值。

撒花完结,感谢大家观看,若有疑问或错误麻烦评论区指出,不胜感激!
Alt

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值