Jakob Jenkov 2020-05-22
Java 的 Iterator 接口表示一个能够遍历集合中对象的对象,一次遍历一个对象。Iterator 接口是 Java 中用于遍历集合的最古老机制之一(尽管不是最早的 —— 在它之前还有 Enumerator)。
要使用 Java Iterator,你需要从你想要遍历的对象集合中获取一个 Iterator 实例。所获得的 Iterator 会跟踪底层集合中的元素,以确保你遍历完所有元素。如果你在通过指向该集合的 Iterator 进行遍历时修改了底层集合,Iterator 通常会检测到这一点,并在你下次尝试从 Iterator 获取下一个元素时抛出异常。稍后将对此进行更详细的说明。
Java Iterator 核心方法
Java Iterator 接口相当简单。其核心方法如下:
| 方法 | 描述 |
|---|---|
hasNext() |
如果 Iterator 还有更多元素,则返回 true;否则返回 false。 |
next() |
返回 Iterator 的下一个元素。 |
remove() |
从 Iterator 所遍历的集合中移除最近由 next() 返回的元素。 |
forEachRemaining() |
遍历 Iterator 中剩余的所有元素,并对每个元素调用传入的 Java Lambda 表达式。 |
以下各节将分别介绍这些方法。
获取 Iterator
大多数情况下,你会通过从包含多个嵌套对象的 Java 对象中获取 Iterator 来使用它。标准 Java 集合接口 Collection 包含一个名为 iterator() 的方法。通过调用 iterator(),你可以从给定的 Collection 中获取一个 Iterator。
你也可以从许多 Java 集合数据结构(例如 List、Set、Map、Queue、Deque 等)中获取 Iterator。
以下是从各种 Java 集合类型中获取 Java Iterator 的几个示例:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
Iterator<String> iterator = list.iterator();
Set<String> set = new HashSet<>();
set.add("one");
set.add("two");
set.add("three");
Iterator<String> iterator2 = set.iterator();
遍历 Iterator
你可以使用 while 循环来遍历 Iterator 中的对象。下面是一个使用 while 循环遍历 Java Iterator 元素的示例:
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
Object nextObject = iterator.next();
}
上面的 Java 示例中有两个方法需要注意:
- 第一个是
Iterator的hasNext()方法,如果 Iterator 还有更多元素则返回true。换句话说,如果 Iterator 尚未遍历完它所来源集合中的所有元素,hasNext()就会返回true;如果已经遍历完所有元素,则返回false。 - 第二个是
next()方法,它返回 Iterator 正在遍历的集合中的下一个元素。
遍历顺序
Java Iterator 中元素的遍历顺序取决于提供该 Iterator 的对象。例如,从 List 获取的 Iterator 会按照 List 内部存储元素的顺序进行遍历。而从 Set 获取的 Iterator 则不保证元素的遍历顺序。
Java List Iterator
以下是从 List 实例获取 Java Iterator 的示例:
List list = new ArrayList();
list.add("123");
list.add("456");
list.add("789");
Iterator iterator = list.iterator();
Java Set Iterator
以下是从 Set 实例获取 Java Iterator 的示例:
Set set = new HashSet();
set.add("123");
set.add("456");
set.add("789");
Iterator iterator = set.iterator();
遍历期间的修改
某些集合不允许你在通过 Iterator 遍历时修改集合。在这种情况下,当你下一次调用 Iterator 的 next() 方法时,会抛出 ConcurrentModificationException。以下示例在执行时会抛出 ConcurrentModificationException:
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String value = iterator.next();
if(value.equals("456")){
list.add("999"); // 抛出 ConcurrentModificationException
}
}
之所以抛出 ConcurrentModificationException,是因为如果你在通过 Iterator 遍历集合的同时修改了集合,Iterator 就会与集合不同步。
遍历期间删除元素
Java Iterator 接口提供了一个 remove() 方法,允许你从底层集合中删除刚刚由 next() 返回的元素。调用 remove() 不会导致抛出 ConcurrentModificationException。以下是在遍历 Iterator 时从集合中删除元素的示例:
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String value = iterator.next();
if(value.equals("456")){
iterator.remove(); // 安全删除
}
}
forEachRemaining()
Java Iterator 的 forEachRemaining() 方法可以内部遍历 Iterator 中剩余的所有元素,并对每个元素调用作为参数传入的 Java Lambda 表达式。以下是使用 forEachRemaining() 的示例:
List<String> list = new ArrayList<>();
list.add("Jane");
list.add("Heidi");
list.add("Hannah");
Iterator<String> iterator = list.iterator();
iterator.forEachRemaining((element) -> {
System.out.println(element);
});
ListIterator
Java 还包含一个名为 ListIterator 的接口,它扩展了 Iterator 接口。ListIterator 表示一个双向迭代器,即你可以向前和向后遍历元素。这里不会详细介绍 ListIterator 接口,但会展示一个快速使用示例:
List<String> list = new ArrayList<>();
list.add("Jane");
list.add("Heidi");
list.add("Hannah");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()) {
System.out.println(listIterator.next());
}
while(listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
如你所见,该示例首先通过所有元素正向遍历 ListIterator,然后再反向遍历回第一个元素。
在自定义类中实现 Iterator 接口
如果你有一个特殊的、自定义的集合类型,你可以自己实现 Java Iterator 接口,以创建一个能遍历你自定义集合元素的 Iterator。本节将展示一个非常简单的 Java Iterator 接口自定义实现,让你了解如何自行实现 Iterator 接口。
我将为一个标准的 Java List 实现一个 Iterator。这个实现并不完美(例如,它无法检测遍历过程中 List 内容的变更),但足以让你了解 Iterator 实现的大致样子:
import java.util.Iterator;
import java.util.List;
public class ListIterator<T> implements Iterator<T> {
private List<T> source = null;
private int index = 0;
public ListIterator(List<T> source){
this.source = source;
}
@Override
public boolean hasNext() {
return this.index < this.source.size();
}
@Override
public T next() {
return this.source.get(this.index++);
}
}
以下是使用上述 ListIterator 进行遍历的示例:
import java.util.ArrayList;
import java.util.List;
public class ListIteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("one");
list.add("two");
list.add("three");
ListIterator<String> iterator = new ListIterator<>(list);
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}