杰瑞科技汇

Java iterator 遍历如何高效且安全?

什么是 Iterator

Iterator 是一个接口,它提供了一种统一的方式来遍历集合(Collection)中的元素,而无需关心集合底层的具体实现(是 ArrayListLinkedList 还是 HashSet)。

Java iterator 遍历如何高效且安全?-图1
(图片来源网络,侵删)

你可以把它想象成一个指向集合元素的光标,它总在元素之间移动,告诉你当前的位置,并允许你获取下一个元素。

Iterator 的核心方法

Iterator 接口主要定义了三个方法:

方法名 描述
boolean hasNext() 判断当前位置的后面是否还有元素,如果有,返回 true;否则返回 false
E next() 获取当前位置的元素,并将迭代器的光标向后移动一位
void remove() 删除迭代器最后返回的那个元素,这是一个可选操作,并非所有集合都支持。

注意:调用 next() 之前,必须先调用 hasNext() 进行判断,如果直接调用 next() 而没有元素了,会抛出 NoSuchElementException 异常。


如何获取和使用 Iterator

我们通过集合对象的 iterator() 方法来获取其对应的 Iterator 实例。

Java iterator 遍历如何高效且安全?-图2
(图片来源网络,侵删)

下面我们通过一个完整的例子来演示如何使用 Iterator 遍历 ArrayList

示例代码:基本遍历

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
    public static void main(String[] args) {
        // 1. 创建一个 List 集合
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        fruits.add("Mango");
        System.out.println("原始集合: " + fruits);
        // 2. 通过 iterator() 方法获取迭代器
        Iterator<String> iterator = fruits.iterator();
        // 3. 使用 while 循环和迭代器遍历集合
        System.out.println("\n使用 Iterator 遍历集合:");
        while (iterator.hasNext()) {
            // 4. 获取当前元素
            String fruit = iterator.next();
            System.out.println(fruit);
            // 你可以在这里对元素进行操作,比如判断
            if ("Orange".equals(fruit)) {
                System.out.println("  -> 我最喜欢的水果是橙子!");
            }
        }
        System.out.println("\n遍历完成后的集合: " + fruits);
    }
}

输出结果:

原始集合: [Apple, Banana, Orange, Mango]
使用 Iterator 遍历集合:
Apple
Banana
Orange
  -> 我最喜欢的水果是橙子!
Mango
遍历完成后的集合: [Apple, Banana, Orange, Mango]

Iteratorremove() 方法

remove() 方法是一个强大的功能,它允许你在遍历时安全地删除元素,它删除的是上一次调用 next() 方法返回的元素

示例代码:遍历时删除元素

假设我们要从集合中删除所有以 "A" 开头的元素。

Java iterator 遍历如何高效且安全?-图3
(图片来源网络,侵删)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorRemoveExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Amy");
        names.add("David");
        names.add("Alex");
        System.out.println("删除前的集合: " + names);
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.startsWith("A")) {
                // 使用迭代器的 remove 方法删除元素
                iterator.remove();
            }
        }
        System.out.println("删除后的集合: " + names);
    }
}

输出结果:

删除前的集合: [Alice, Bob, Amy, David, Alex]
删除后的集合: [Bob, David]

⚠️ 重要警告

  1. 不能使用集合的 remove() 方法:如果在迭代过程中使用集合自身的 remove() 方法(如 list.remove("Alice")),会抛出 ConcurrentModificationException(并发修改异常),这是因为迭代器在遍历时会维护一个“预期修改计数”,而集合的 remove() 方法会改变这个计数,导致迭代器检测到数据被“意外”修改。
  2. remove() 的时机remove() 必须紧跟在 next() 之后调用,如果在没有调用 next() 或已经调用了 next() 但又调用了下一次 next() 之后调用 remove(),同样会抛出 IllegalStateException

Iterator vs. for-each 循环

在现代 Java 开发中,我们更常用 for-each 循环(也称为增强 for 循环)来遍历集合或数组。

for-each 循环示例

List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
System.out.println("使用 for-each 循环遍历:");
for (String fruit : fruits) {
    System.out.println(fruit);
}

for-each 循环的本质

for-each 循环只是 Iterator 的一个语法糖(Syntactic Sugar),编译器在后台会自动将 for-each 循环转换成使用 Iteratorwhile 循环。

下面两段代码在编译后是等价的:

// 代码 1: for-each 循环
for (String fruit : fruits) {
    // do something
}
// 代码 2: 编译器转换后的等价代码
for (Iterator<String> i = fruits.iterator(); i.hasNext();) {
    String fruit = i.next();
    // do something
}

Iterator vs. ListIterator

ListIteratorIterator 的一个子接口,专门用于遍历 List 集合,它提供了比 Iterator 更强大的功能:

特性 Iterator ListIterator
适用范围 所有 Collection List
遍历方向 只能向后 可以向前,也可以向后
获取索引 不支持 支持 nextIndex()previousIndex()
添加元素 不支持 支持 add(E e) 方法
修改元素 不支持 支持 set(E e) 方法

ListIterator 示例

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("A");
        names.add("B");
        names.add("C");
        ListIterator<String> listIterator = names.listIterator();
        System.out.println("向后遍历:");
        while (listIterator.hasNext()) {
            System.out.println(listIterator.nextIndex() + ": " + listIterator.next());
        }
        System.out.println("\n向前遍历:");
        while (listIterator.hasPrevious()) {
            System.out.println(listIterator.previousIndex() + ": " + listIterator.previous());
        }
        System.out.println("\n在 'B' 后插入 'X'");
        // 将光标移动到 'B' 的后面
        listIterator.next(); // A
        listIterator.next(); // B
        listIterator.add("X"); // 在 B 后面插入 X
        System.out.println("插入后的集合: " + names);
    }
}

输出结果:

向后遍历:
0: A
1: B
2: C
向前遍历:
2: C
1: B
0: A
在 'B' 后插入 'X'
插入后的集合: [A, B, X, C]

总结与最佳实践

  1. 何时使用 Iterator

    • 当你需要在遍历过程中安全地删除元素时,必须使用 Iteratorremove() 方法。
    • 当你需要双向遍历ListIterator)或在遍历时添加/修改元素时,使用 ListIterator
  2. 何时使用 for-each 循环?

    • 绝大多数情况下,for-each 循环是遍历集合或数组的首选和最佳方式,因为它代码更简洁、可读性更高,并且能避免 ConcurrentModificationException
    • 只在你需要 Iterator 的特殊功能(如 removeadd、双向遍历)时,才显式地使用 IteratorListIterator
  3. 核心思想

    • Iterator 提供了解耦:遍历逻辑与集合的具体实现分离开,使得遍历算法可以适用于任何集合。
    • 它是 Java 集合框架“面向接口编程”思想的完美体现。
分享:
扫描分享到社交APP
上一篇
下一篇