杰瑞科技汇

Java iterator底层如何实现遍历机制?

Iterator 是 Java 集合框架(Java Collections Framework, JCF)中的一个核心接口,它提供了一种统一的方式来遍历集合中的元素,而无需关心集合底层的具体实现,这种“解耦”是 Iterator 设计的精髓。

Java iterator底层如何实现遍历机制?-图1
(图片来源网络,侵删)

Iterator 接口的核心方法

我们来看一下 java.util.Iterator 接口本身,它定义了三个核心方法:

public interface Iterator<E> {
    // 判断是否有下一个元素,如果遍历到达集合末尾,返回 false
    boolean hasNext();
    // 返回迭代的下一个元素,并将指针向后移动一位
    E next();
    // (可选操作)从迭代器指向的集合中移除当前元素
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
}
  • hasNext(): 这是一个判断方法,用于检查当前位置是否还有下一个元素可以迭代,在调用 next() 之前,通常应该先调用 hasNext() 来避免抛出 NoSuchElementException
  • next(): 这个方法会返回当前指针指向的元素,并将指针移动到下一个位置。
  • remove(): 这是一个可选的删除操作,它用于移除由 next() 方法最近返回的元素。重要的一点是remove() 必须在 next() 调用之后立即调用,且每个 next() 最多只能调用一次 remove(),如果直接调用而没有先调用 next(),会抛出 IllegalStateException

Iterable 接口与 for-each 循环

Iterator 通常不是直接被创建和使用的,它通过另一个接口 java.lang.Iterable 来暴露。

public interface Iterable<T> {
    // 返回一个在此集合元素上进行迭代的迭代器
    Iterator<T> iterator();
}

Iterable 的作用是:

  1. 提供 iterator() 方法:任何实现了 Iterable 接口的类,都可以通过 iterator() 方法获得一个 Iterator 实例。
  2. 支持 for-each 循环:在 Java 中,for-each 循环的语法糖底层就是依赖于 Iterable 接口,编译器会将 for-each 循环转换成基于 iterator()while 循环。

举个例子,for-each 循环的“幕后真相”:

Java iterator底层如何实现遍历机制?-图2
(图片来源网络,侵删)

假设你有这样一段代码:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

编译器会将其转换为类似这样的代码:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 1. 调用 names.iterator() 获取迭代器
Iterator<String> iterator = names.iterator();
// 2. 使用 while 循环和迭代器的方法
while (iterator.hasNext()) {
    // 3. 调用 next() 获取元素
    String name = iterator.next();
    System.out.println(name);
}

一个类如果想支持 for-each 循环,就必须实现 Iterable 接口,Java 中所有主要的集合类,如 ArrayList, LinkedList, HashSet, HashMapkeySet()values() 等都实现了 Iterable 接口。


如何自定义一个 Iterator 实现

理解了 IterableIterator 的关系后,我们来看一个实际的例子:如何为一个自定义的集合类实现迭代功能。

假设我们想实现一个简单的“动态数组” MyCustomList,并让它支持 for-each 循环。

步骤 1: 创建自定义集合类 MyCustomList

这个类将内部存储一个数组,并实现 Iterable 接口。

import java.util.Iterator;
// 1. 实现 Iterable 接口,表明这个类可以被迭代
public class MyCustomList<T> implements Iterable<T> {
    private Object[] elements; // 使用 Object 数组存储元素
    private int size = 0;      // 当前元素数量
    public MyCustomList(int initialCapacity) {
        this.elements = new Object[initialCapacity];
    }
    public void add(T element) {
        if (size == elements.length) {
            // 简单的扩容逻辑
            Object[] newElements = new Object[elements.length * 2];
            System.arraycopy(elements, 0, newElements, 0, size);
            elements = newElements;
        }
        elements[size++] = element;
    }
    public int size() {
        return size;
    }
    // 2. 实现 iterator() 方法,返回一个 Iterator 实例
    @Override
    public Iterator<T> iterator() {
        return new MyCustomListIterator(); // 返回我们自定义的迭代器
    }
    // 3. 创建一个内部类来实现 Iterator 接口
    private class MyCustomListIterator implements Iterator<T> {
        private int currentIndex = 0; // 当前迭代的位置
        @Override
        public boolean hasNext() {
            // 只要当前位置小于集合大小,就说明还有元素
            return currentIndex < size;
        }
        @Override
        public T next() {
            // 如果没有下一个元素,抛出异常
            if (!hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            // 返回当前元素,并将索引加一
            @SuppressWarnings("unchecked")
            T element = (T) elements[currentIndex];
            currentIndex++;
            return element;
        }
        // (可选) 实现 remove 方法
        @Override
        public void remove() {
            // 为了简单起见,这里我们实现一个简单的删除逻辑
            // 注意:这是一个简化的实现,生产环境需要处理更复杂的情况(如并发修改)
            if (currentIndex == 0) {
                throw new IllegalStateException("next() has not been called yet or remove() already called");
            }
            // 移除元素:将后面的元素前移
            for (int i = currentIndex - 1; i < size - 1; i++) {
                elements[i] = elements[i + 1];
            }
            elements[size - 1] = null; // 清理最后一个元素
            size--; // 集合大小减一
            currentIndex--; // 因为元素被移除了,当前索引需要回退一位
        }
    }
}

步骤 2: 使用自定义的 MyCustomList

现在我们可以像使用普通集合一样使用 MyCustomList,并享受 for-each 循环带来的便利。

public class Main {
    public static void main(String[] args) {
        MyCustomList<String> names = new MyCustomList<>(3);
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David"); // 触发扩容
        System.out.println("--- 使用 for-each 循环遍历 ---");
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("\n--- 使用迭代器并移除元素 ---");
        // 获取一个新的迭代器
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if ("Bob".equals(name)) {
                System.out.println("找到 Bob,准备移除...");
                iterator.remove(); // 使用迭代器的 remove 方法
            }
        }
        System.out.println("\n--- 移除 Bob 后再次遍历 ---");
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("当前列表大小: " + names.size()); // 应该输出 3
    }
}

输出结果:

--- 使用 for-each 循环遍历 ---
Alice
Bob
Charlie
David
--- 使用迭代器并移除元素 ---
找到 Bob,准备移除...
--- 移除 Bob 后再次遍历 ---
Alice
Charlie
David
当前列表大小: 3

Iterator 的设计原则与注意事项

  1. Fail-Fast 机制 大多数 Java 集合的迭代器都是 "fail-fast" 的,这意味着,如果在迭代过程中,除了迭代器自身的 remove() 方法外,集合被任何其他方式(比如直接调用 list.remove())修改了,迭代器会立即检测到并发修改,并抛出 ConcurrentModificationException 异常。

    这是为了保证迭代过程的安全性和一致性,在上面的 MyCustomList 示例中,我们没有实现 fail-fast 机制,这是一个简化的版本,标准的 ArrayList 迭代器内部会维护一个 modCount 字段,用于检测并发修改。

  2. remove() 方法的时机 再次强调,iterator.remove() 必须紧跟在 iterator.next() 之后调用,它移除的是 next() 返回的那个元素,你不能连续调用两次 remove(),也不能在没有调用 next() 的情况下调用 remove()

  3. for-each 循环与 remove() for-each 循环内部是使用了迭代器的,如果你想在 for-each 循环中安全地删除元素,应该使用迭代器自身的 remove() 方法,而不是直接调用集合的 remove() 方法,否则几乎肯定会触发 ConcurrentModificationException

    错误示例:

    List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
    for (String s : list) {
        if ("B".equals(s)) {
            list.remove(s); // 会抛出 ConcurrentModificationException
        }
    }

    正确示例:

    List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String s = iterator.next();
        if ("B".equals(s)) {
            iterator.remove(); // 正确!
        }
    }
概念 作用 关键点
Iterator 迭代器接口,定义了遍历集合的统一方法 (hasNext(), next(), remove())。 它是遍历操作的“执行者”。
Iterable 可迭代接口,提供了 iterator() 方法。 它是遍历操作的“入口”,是支持 for-each 循环的前提。
关系 Iterable 通过 iterator() 方法返回一个 Iterator 实例。 for-each 循环的底层实现就是 Iterable -> iterator() -> while(hasNext())
自定义实现 创建集合类实现 Iterable
iterator() 方法中返回一个实现了 Iterator 接口的内部类实例。
内部类需要维护当前迭代的位置(索引等),并实现 hasNext()next() 逻辑。

通过实现 IterableIterator,你可以为任何自定义的数据结构提供强大且标准的遍历能力,使其无缝地融入到 Java 的生态系统中。

分享:
扫描分享到社交APP
上一篇
下一篇