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

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 的作用是:
- 提供
iterator()方法:任何实现了Iterable接口的类,都可以通过iterator()方法获得一个Iterator实例。 - 支持
for-each循环:在 Java 中,for-each循环的语法糖底层就是依赖于Iterable接口,编译器会将for-each循环转换成基于iterator()的while循环。
举个例子,for-each 循环的“幕后真相”:

假设你有这样一段代码:
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, HashMap 的 keySet() 和 values() 等都实现了 Iterable 接口。
如何自定义一个 Iterator 实现
理解了 Iterable 和 Iterator 的关系后,我们来看一个实际的例子:如何为一个自定义的集合类实现迭代功能。
假设我们想实现一个简单的“动态数组” 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 的设计原则与注意事项
-
Fail-Fast 机制 大多数 Java 集合的迭代器都是 "fail-fast" 的,这意味着,如果在迭代过程中,除了迭代器自身的
remove()方法外,集合被任何其他方式(比如直接调用list.remove())修改了,迭代器会立即检测到并发修改,并抛出ConcurrentModificationException异常。这是为了保证迭代过程的安全性和一致性,在上面的
MyCustomList示例中,我们没有实现fail-fast机制,这是一个简化的版本,标准的ArrayList迭代器内部会维护一个modCount字段,用于检测并发修改。 -
remove()方法的时机 再次强调,iterator.remove()必须紧跟在iterator.next()之后调用,它移除的是next()返回的那个元素,你不能连续调用两次remove(),也不能在没有调用next()的情况下调用remove()。 -
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() 逻辑。 |
通过实现 Iterable 和 Iterator,你可以为任何自定义的数据结构提供强大且标准的遍历能力,使其无缝地融入到 Java 的生态系统中。
