Java迭代器案例:如何高效遍历集合?从原理到实战完全指南
📚 目录导读
- 什么是Java迭代器? —— 核心概念与设计思想
- 迭代器 vs 增强for循环 vs 普通for循环 —— 三大遍历方式对比
- Iterator接口详解 —— hasNext()、next()、remove()方法实战
- 实战案例:遍历ArrayList、HashSet、HashMap —— 不同集合的迭代器使用
- 迭代器常见错误与陷阱 —— ConcurrentModificationException等
- Java 8+的迭代器进化 —— 迭代器与Stream API、forEachRemaining()
- 高频面试问答 —— 你可能遇到的10个问题
什么是Java迭代器?
迭代器(Iterator) 是Java集合框架中的一种设计模式,它提供了一种统一的方式来遍历集合中的元素,而不需要暴露集合的内部结构,迭代器就像一个“指针”,可以顺序访问集合中的每个元素。

为什么需要迭代器?
- 解耦遍历逻辑:无论集合是数组、链表还是哈希表,迭代器提供相同的遍历接口。
- 支持失败快速机制:当集合被并发修改时,迭代器会抛出
ConcurrentModificationException,避免数据不一致。 - 可删除元素:通过迭代器的
remove()方法可以安全地在遍历过程中删除元素。
官方定义:java.util.Iterator是Java Collections Framework的核心接口,所有集合类(如List、Set、Queue)都提供了iterator()方法返回迭代器对象。
迭代器 vs 增强for循环 vs 普通for循环
很多开发者会问:“既然有了for-each,为什么还要用迭代器?” 我们用一个表格对比:
| 遍历方式 | 适用场景 | 能否删除元素 | 是否支持修改 | 效率 |
|---|---|---|---|---|
| 普通for循环 | List(有索引) | 可以,但易出错 | 可修改 | 高 |
| 增强for循环 | 所有Collection | ❌ 不能 | 不可修改 | 中 |
| 迭代器 | 所有Collection | ✅ 可以安全删除 | 中 |
核心区别:当需要在遍历的同时删除元素时,必须使用迭代器,使用for-each直接删除会抛出ConcurrentModificationException。
Iterator接口核心方法
public interface Iterator<E> {
boolean hasNext(); // 判断是否还有下一个元素
E next(); // 返回下一个元素,并将指针后移
default void remove() { throw new UnsupportedOperationException(); } // 移除当前元素
default void forEachRemaining(Consumer<? super E> action) { ... } // Java 8新增
}
典型使用模式:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
// 处理element
if ("要删除的元素".equals(element)) {
iterator.remove(); // 安全删除
}
}
⚠️ 注意:
remove()必须在next()之后调用,且每次next()后只能调用一次remove()。
实战案例:遍历不同集合
案例1:遍历ArrayList(线性表)
import java.util.*;
public class ArrayListIteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("Java", "Python", "Go", "Rust"));
// 使用迭代器遍历并删除长度大于3的元素
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String lang = it.next();
if (lang.length() > 3) {
it.remove();
}
}
System.out.println("删除后: " + list); // 输出: [Go]
}
}
案例2:遍历HashSet(无重复元素)
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
Integer num = it.next();
if (num % 2 == 0) {
System.out.print(num + " "); // 输出偶数: 2 4 6
}
}
注意:Set迭代器的元素顺序不保证与插入顺序一致,除非使用
LinkedHashSet。
案例3:遍历HashMap(键值对)
HashMap本身不直接支持迭代器,但可以通过entrySet()获取键值对集合再遍历:
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
scores.put("Charlie", 95);
// 方式1:遍历Entry
Iterator<Map.Entry<String, Integer>> entryIt = scores.entrySet().iterator();
while (entryIt.hasNext()) {
Map.Entry<String, Integer> entry = entryIt.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
if (entry.getValue() < 90) {
entryIt.remove(); // 删除分数低于90的条目
}
}
// 方式2:遍历Key(不常用)
Iterator<String> keyIt = scores.keySet().iterator();
常见错误与陷阱
陷阱1:在for-each中删除元素
// ❌ 错误代码
for (String s : list) {
if ("Python".equals(s)) {
list.remove(s); // 会抛出ConcurrentModificationException
}
}
// ✅ 正确做法:使用迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("Python".equals(it.next())) {
it.remove();
}
}
陷阱2:忘记调用next()就调用remove()
Iterator<String> it = list.iterator(); it.remove(); // ❌ IllegalStateException,必须先next()
陷阱3:多线程环境下的并发修改
如果多个线程同时修改集合,建议使用CopyOnWriteArrayList或ConcurrentHashMap,或者在遍历时加锁。
Java 8+的迭代器进化
forEachRemaining()方法
Java 8为Iterator新增了默认方法,可以一次性处理剩余所有元素:
Iterator<String> it = list.iterator(); it.next(); // 跳过第一个元素 it.forEachRemaining(System.out::println); // 打印剩余所有元素
迭代器与Stream API
Spliterator(可拆分迭代器)是Stream并行处理的底层基础,我们可以将迭代器转为Stream:
import java.util.stream.StreamSupport;
import java.util.Spliterators;
Iterator<String> it = list.iterator();
Stream<String> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(it, 0), false);
stream.filter(s -> s.startsWith("J")).forEach(System.out::println);
使用Iterable接口自定义可遍历类
public class MyCollection<T> implements Iterable<T> {
private List<T> items = new ArrayList<>();
@Override
public Iterator<T> iterator() {
return items.iterator();
}
// 增强for循环可以直接使用
public static void main(String[] args) {
MyCollection<String> mc = new MyCollection<>();
for (String s : mc) { // 自动调用iterator()
System.out.println(s);
}
}
}
高频面试问答(问答形式)
Q1:迭代器与for-each循环有什么区别?
A:for-each本质上是语法糖,底层仍然使用迭代器(编译后会生成Iterator代码),但for-each无法在遍历时删除元素,也无法获取迭代器的位置信息,迭代器更灵活,支持删除和双向遍历(ListIterator)。
Q2:什么是“快速失败”机制?
A:当迭代器遍历集合时,如果集合结构被修改(如增加/删除元素)且不是通过迭代器自身的remove()方法,迭代器会立即抛出ConcurrentModificationException,这是通过维护一个modCount计数器实现的,每次修改集合都会增加该值,迭代器会检查是否匹配。
Q3:如何在遍历时安全删除元素?
A:只能使用迭代器的remove()方法,或者使用Java 8的Collection.removeIf()方法,它底层也是通过迭代器实现的。
Q4:为什么不能在for-each中修改集合?
A:for-each循环在编译后会生成迭代器代码,但你没有迭代器引用,无法调用remove(),而直接调用list.remove()会修改集合的modCount,导致迭代器在下次检查时抛出异常。
Q5:Iterator和ListIterator有什么区别?
A:ListIterator是Iterator的子接口,专门用于List,它支持:
- 双向遍历(hasPrevious()和previous())
- 获取索引(nextIndex()和previousIndex())
- 修改元素(set()方法)
- 添加元素(add()方法)
Q6:迭代器遍历HashMap时,顺序是确定的吗?
A:对于普通HashMap,顺序不确定(取决于哈希分布),如果需要有序遍历,使用LinkedHashMap(按插入顺序)或TreeMap(按Key排序)。
Q7:如何遍历一个数组?
A:数组本身没有迭代器,但可以通过Arrays.asList()转为List再获取迭代器,或者直接使用普通for循环。
Q8:forEachRemaining()和while(hasNext())哪种更好?
A:功能相同,但forEachRemaining更简洁,适合需要一次性处理剩余所有元素的场景,在并行流处理时,forEachRemaining可能更高效。
Q9:迭代器是fail-safe还是fail-fast?
A:Java集合框架中的迭代器默认是fail-fast(快速失败),但Java也在java.util.concurrent包中提供了并发安全的迭代器(如CopyOnWriteArrayList的迭代器),它们是fail-safe的,允许在遍历时修改集合。
Q10:如何用迭代器实现一个简单的循环?
A:可以使用ListIterator实现双向循环,或者使用Iterator配合while循环,注意检查hasNext()避免无限循环。
Java迭代器是遍历集合的“瑞士军刀”,掌握它能让你:
- ✅ 安全删除元素
- ✅ 统一所有集合的遍历方式
- ✅ 理解并发修改异常的产生原因
- ✅ 从原理上理解for-each的机制
最佳实践:
- 需要删除元素时 → 使用Iterator
- 只需要读取元素 → 使用for-each(更简洁)
- 需要双向遍历或修改 → 使用ListIterator
- Java 8及以上 → 考虑使用Stream API(更函数式)
扩展阅读:Enumeration接口(较老)、Spliterator(用于并行流)、Iterable接口(用于自定义集合)。