迭代器源码分析(基于 ArrayList)

迭代器源码分析(基于 ArrayList

iterator源码分析

1. 迭代器的基本定义

ArrayList 中,调用 list.iterator() 时,实际上是通过 ArrayList 内部的 Itr 类实现的迭代器。其源码如下:

public Iterator<E> iterator() {
    
    
    return new Itr();  // 返回迭代器的实例
}

此时,会创建 Itr 类的一个实例,这个类实现了 Iterator 接口,并负责对 ArrayList 进行元素遍历。

默认初始化:cursor = 0,所以游标指向 0 索引位置。


2. 迭代器的核心方法

2.1 hasNext() 方法

hasNext() 方法用于检查迭代器是否还有未被遍历的元素,它的实现逻辑非常简单,只需判断当前指针 cursor 是否到达数组末尾。

public boolean hasNext() {
    
    
    return cursor != size; // 如果游标没有超出元素数量,表示还有元素可以遍历
}
2.2 next() 方法

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 用来跟踪当前的遍历位置,每调用一次 next(),游标就会右移,直到所有元素被遍历完成。

2.3 remove() 方法

remove() 用于删除最后一次调用 next() 时返回的元素:

public void remove() {
    
    
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();
    
    try {
    
    
        ArrayList.this.remove(lastRet); // 调用 `ArrayList` 的删除方法
        cursor = lastRet;               // 更新游标位置
        lastRet = -1;                   // 重置 lastRet
        expectedModCount = modCount;    // 更新修改计数
    } catch (IndexOutOfBoundsException ex) {
    
    
        throw new ConcurrentModificationException();
    }
}

该方法通过内部的 ArrayListremove() 方法来完成元素删除,删除后游标 cursor 会重置为删除元素的位置。


3. 内部状态及安全机制

3.1 cursorlastRet
  • cursor:表示迭代器当前指向的索引位置。
  • lastRet:表示最后一次通过 next() 方法返回的元素索引,初始值为 -1
3.2 modCountexpectedModCount
  • modCount:记录集合结构修改的次数。

  • expectedModCount:迭代器期望的修改次数。每次创建迭代器时,它会将 modCount 赋值给 expectedModCount。如果在遍历过程中,集合发生了结构修改(如增加、删除元素),这两个值不再相等,会抛出 ConcurrentModificationException 异常,确保遍历的安全性。

final void checkForComodification() {
    
    
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
3.3 并发修改异常

在遍历集合时,如果直接使用集合的 add()remove() 等方法,会导致 modCount 发生变化,从而引发 ConcurrentModificationException 异常。

总结:
  1. 迭代器封装了 ArrayList 的遍历操作,确保集合遍历的安全性和统一性。
  2. 使用迭代器时,避免直接通过集合方法修改集合,否则可能引发并发修改异常。

ListIterator 源码分析

ListIteratorIterator 的子接口,专为 List 设计,提供了在双向遍历列表和修改元素方面的更多功能。与 Iterator 不同,ListIterator 可以在遍历过程中向前或向后移动,并允许元素修改、添加等操作。


1. ListIterator 的基本功能

ListIterator 是一个功能增强版的迭代器,提供了如下功能:

  1. 双向遍历:可以通过 hasPrevious()previous() 方法向前遍历列表。
  2. 元素修改:可以通过 set() 方法修改列表中的元素。
  3. 添加元素:可以通过 add() 方法在列表中添加元素。

2. ArrayList 中的 ListIterator 实现

ArrayList 中,通过 list.listIterator() 方法可以获取 ListIterator 实例。内部实现与普通迭代器类似,不过提供了更多的方法,支持双向遍历和元素修改。

2.1 创建 ListIterator

我们来看 ArrayListListIterator 的创建方法:

public ListIterator<E> listIterator(int index) {
    
    
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);  // 返回 ListItr 实例
}

可以看到,ListItr 类是 ListIterator 的实现类。该方法会根据传入的索引创建一个 ListItr 实例,并将当前遍历位置设置为指定的索引。

2.2 ListItr 类的定义

ListItr 继承自 Itr,也就是普通的迭代器类,并在此基础上添加了双向遍历和修改列表的功能:

private class ListItr extends Itr implements ListIterator<E> {
    
    
    ListItr(int index) {
    
    
        super();  // 调用父类的构造方法
        cursor = index;  // 设置游标为指定位置
    }

    public boolean hasPrevious() {
    
    
        return cursor != 0;  // 游标是否位于列表开头
    }

    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];  // 返回前一个元素,并更新 lastRet
    }

    public int nextIndex() {
    
    
        return cursor;
    }

    public int previousIndex() {
    
    
        return cursor - 1;
    }

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

        try {
    
    
            ArrayList.this.set(lastRet, e);  // 调用 ArrayList 的 set 方法
        } catch (IndexOutOfBoundsException ex) {
    
    
            throw new ConcurrentModificationException();
        }
    }

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

        try {
    
    
            int i = cursor;
            ArrayList.this.add(i, e);  // 调用 ArrayList 的 add 方法
            cursor = i + 1;            // 游标前移
            lastRet = -1;
            expectedModCount = modCount;  // 更新 expectedModCount
        } catch (IndexOutOfBoundsException ex) {
    
    
            throw new ConcurrentModificationException();
        }
    }
}

3. ListIterator 的核心方法

3.1 hasPrevious()previous()

这些方法用于向前遍历列表。hasPrevious() 检查是否存在前一个元素,previous() 则返回前一个元素,并将游标向前移动。

public boolean hasPrevious() {
    
    
    return cursor != 0;  // 如果游标不在开头,就有前一个元素
}

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];  // 返回前一个元素
}
3.2 nextIndex()previousIndex()

这两个方法返回当前游标所指向的下一个和前一个元素的索引,分别用于向前和向后遍历时的索引位置:

public int nextIndex() {
    
    
    return cursor;  // 当前游标即为下一个元素的索引
}

public int previousIndex() {
    
    
    return cursor - 1;  // 当前游标减一即为前一个元素的索引
}
3.3 set()add()

set() 方法允许在列表中修改最近一次通过 next()previous() 返回的元素:

public void set(E e) {
    
    
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();
    ArrayList.this.set(lastRet, e);  // 通过 ArrayList 的 set 方法修改元素
}

add() 方法允许在列表中添加新元素,添加后的元素位于当前游标位置:

public void add(E e) {
    
    
    checkForComodification();
    int i = cursor;
    ArrayList.this.add(i, e);  // 在游标位置添加元素
    cursor = i + 1;            // 游标前移
    lastRet = -1;              // 重置 lastRet
    expectedModCount = modCount;  // 更新 expectedModCount
}

4. ListIterator 的并发安全性

Iterator 类似,ListIterator 同样使用 modCountexpectedModCount 来检测集合的结构是否发生了并发修改。如果在迭代过程中检测到 modCountexpectedModCount 不一致,就会抛出 ConcurrentModificationException 异常。

final void checkForComodification() {
    
    
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

这个机制确保在遍历过程中,如果集合被其他线程或方法修改,能够立刻发现并抛出异常。


5. ListIterator 的使用示例

下面是一个简单的使用 ListIterator 遍历和修改 ArrayList 的示例:

ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
    
    
    String str = it.next();
    if ("B".equals(str)) {
    
    
        it.set("BB");  // 修改元素
        it.add("BBB"); // 添加新元素
    }
}

System.out.println(list);

输出结果:

[A, BB, BBB, C]

可以看到,使用 ListIterator 能够灵活地在遍历过程中修改或添加元素。

ator it = list.listIterator();
while (it.hasNext()) {
String str = it.next();
if (“B”.equals(str)) {
it.set(“BB”); // 修改元素
it.add(“BBB”); // 添加新元素
}
}

System.out.println(list);


输出结果:

```java
[A, BB, BBB, C]

可以看到,使用 ListIterator 能够灵活地在遍历过程中修改或添加元素。

猜你喜欢

转载自blog.csdn.net/PQ781826/article/details/143391517