CodeGym /课程 /JAVA 25 SELF /Iterable 和 Iterator:遍历集合

Iterable 和 Iterator:遍历集合

JAVA 25 SELF
第 27 级 , 课程 1
可用

1. Iterable 接口

在 Java 中,几乎所有集合(除 Map 外)都实现了 Iterable 接口。这意味着它们可以被顺序遍历——一个接一个,而无需关心内部结构的细节。对程序员来说,这就像“集合自带一种遍历所有元素的方式”。

Iterable 接口只定义了一个方法:

Iterator<E> iterator();

iterator() 方法返回一个 Iterator 对象——一个“助手”,它知道如何一步步遍历集合。得益于它,常见的 for-each 循环才能工作:

for (ElementType e : collection) {
    // ...
}

——在幕后正是这个 Iterator 在工作。额外的好处是:借助它可以在遍历时通过 remove() 安全删除元素。如果用普通遍历直接删除,很容易触发 ConcurrentModificationException

2. Iterator 接口

Iterator 就像一个“快递员”,能够按照集合的遍历顺序逐个访问你的元素而不遗漏。

方法 说明
boolean hasNext()
是否还有下一个可遍历的元素?
E next()
返回下一个元素并移动到该位置
void remove()
安全删除当前元素,不会出错

通过 Iterator 遍历集合的示例

import java.util.*;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("给猫咪顺毛");
        tasks.add("做作业");
        tasks.add("看剧");

        Iterator<String> it = tasks.iterator();
        while (it.hasNext()) {
            String task = it.next();
            System.out.println("任务: " + task);
        }
    }
}

这里发生了什么?

  • 通过 tasks.iterator() 获取迭代器。
  • 当存在下一个元素(hasNext() 返回 true)时,通过 next() 取出并输出。
  • 迭代器会自行维护遍历顺序——你无需知道集合内部如何存储元素。

3. 有了循环为什么还需要 Iterator?

使用 Iterator 可以遍历任何集合,即使它没有下标(例如 Set)。这是一种与具体集合类型无关的通用方式。

安全删除元素

常见需求:遍历集合并删除某些元素。如果用 for-each 来做,可能会遇到错误:

for (String task : tasks) {
    if (task.contains("猫")) {
        tasks.remove(task); // 砰!ConcurrentModificationException
    }
}

为什么会这样? 在由迭代器发起的遍历过程中,集合并不期望其结构被直接修改。

正确做法:

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("猫")) {
        it.remove(); // 一切顺利!
    }
}

为什么不能直接使用下标?

因为并非所有集合都有下标。例如,HashSetTreeSet 并不存在“第 5 个元素”的概念。Iterator 在任何集合上都可用——这正是它的强大之处。

4. for-each 的细节

增强 for 循环(for-each)早在 Java 5 就出现了。本质上它是语法糖,使遍历元素变得尽可能简单:

for (String task : tasks) {
    System.out.println("任务: " + task);
}

在底层,编译器会调用 iterator(),通过 hasNext() 检查并用 next() 取出元素。读起来几乎就像一句自然语言:“对于列表中的每个任务”。

什么时候不适合用 for-each?

  • 需要在遍历过程中删除元素(for-each 不能直接调用 remove())。
  • 需要访问下标,例如按位置替换元素。
  • 你在使用 Map——它是“键-值”对,遍历需要专门的逻辑。

5. 遍历 Map:技巧与注意事项

Map 接口并未直接实现 Iterable,因为它是“键-值”对的集合。尽管如此,Map 提供了便捷的视图用于遍历。

按键遍历

Map<String, String> users = new HashMap<>();
users.put("bob", "bob@example.com");
users.put("alice", "alice@gmail.com");

for (String login : users.keySet()) {
    System.out.println("登录名: " + login);
}

按值遍历

for (String email : users.values()) {
    System.out.println("Email: " + email);
}

按键值对遍历

最通用的方式是遍历 entrySet()

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("登录名: " + entry.getKey() + ", Email: " + entry.getValue());
}

有趣的事实: EntryMap 的内部接口,拥有 getKey()getValue() 方法。这样你可以同时获得键和值。

通过 Iterator 遍历

Iterator<Map.Entry<String, String>> it = users.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, String> entry = it.next();
    // 甚至可以安全地删除元素:
    if (entry.getKey().startsWith("v")) {
        it.remove();
    }
}

6. 实战示例:遍历集合如何帮助你的应用

示例:输出用户的全部任务

List<String> tasks = new ArrayList<>();
tasks.add("做作业");
tasks.add("给猫咪顺毛");
tasks.add("看剧");

System.out.println("你今天的任务:");
for (String task : tasks) {
    System.out.println("- " + task);
}

现在删除所有包含 "猫" 的任务:

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("猫")) {
        it.remove();
    }
}
System.out.println("剩余任务:");
for (String task : tasks) {
    System.out.println("- " + task);
}

示例:通过 Set 遍历唯一登录名

Set<String> logins = new HashSet<>();
logins.add("bob");
logins.add("alice");
logins.add("masha");

for (String login : logins) {
    System.out.println("用户: " + login);
}

注意:Set 的输出顺序可能是任意的!

示例:遍历 Map 以显示用户

Map<String, String> users = new HashMap<>();
users.put("bob", "bob@example.com");
users.put("alice", "alice@gmail.com");

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("登录名: " + entry.getKey() + ", Email: " + entry.getValue());
}

7. Iterator.remove(): 安全地删除元素

初学者最常见的错误之一,是试图在 for-each 遍历时删除元素。迭代器通过 remove() 解决了这一问题。

工作原理:

  • 调用 it.remove() 会删除当前元素——即上一次 next() 返回的那个。
  • 这是安全的:集合不会抛出 ConcurrentModificationException

示例:

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) {
        it.remove(); // 删除所有偶数
    }
}
System.out.println(numbers); // [1, 3, 5]

集合遍历示意图

+---------+     +---------+     +---------+
| Element | --> | Element | --> | Element | ...
+---------+     +---------+     +---------+
     ^               ^
     |               |
   next()         next()

迭代器会按元素逐步前进,直到 hasNext() 返回 false

9. 使用 Iterator 和遍历集合时的常见错误

错误 №1:在 for-each 遍历期间修改集合。
尝试在 for-each 内直接删除元素会导致 ConcurrentModificationException

for (String task : tasks) {
    if (task.contains("猫")) {
        tasks.remove(task); // 砰!ConcurrentModificationException
    }
}

请使用 Iterator 及其 remove()

错误 №2:在 next() 之前调用 remove()。
必须先通过 next() 获取当前元素,否则迭代器不知道要删除哪个。

Iterator<String> it = tasks.iterator();
it.remove(); // 错误!必须先调用 next()

错误 №3:试图直接在 for-each 中遍历 Map。
Map 并未直接实现 Iterable——请使用 keySet()values()entrySet()

Map<String, String> users = new HashMap<>();
// for (String entry : users) { ... } // 错误:不能这样
for (Map.Entry<String, String> e : users.entrySet()) {
    // 正确写法
}

错误 №4:用迭代器遍历时在迭代器之外修改集合。
遍历过程中只能通过 it.remove() 删除元素,不能通过集合的方法直接删除。

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("猫")) {
        tasks.remove(task); // 错误!应该使用 it.remove()
    }
}
评论 (1)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
ncksllpo 级别 44,Cherkasy,Ukraine
17 二月 2026
😀