Jakob Jenkov 2020-05-25
Java 的 Iterable 接口表示一个可迭代的对象集合——也就是说,该集合中的元素可以被遍历。实现 Iterable 接口的类,其元素就可以通过多种方式进行迭代。
你可以通过以下三种方式遍历一个 Iterable 对象:
- 使用 for-each 循环
- 通过调用
iterator()方法获取一个 Java Iterator - 调用
Iterable的forEach()方法
本文将依次展示这三种遍历方式的示例。
使用 for-each 循环遍历 Iterable
遍历 Iterable 元素的第一种方式是使用 Java for-each 循环。下面是一个通过 for-each 循环遍历 Java List 的示例:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
for (String element : list) {
System.out.println(element.toString());
}
此示例首先创建了一个新的 List 并添加了三个元素,然后使用 for-each 循环遍历列表中的每个元素,并打印其 toString() 值。
通过 Iterator 遍历 Iterable
第二种方式是通过调用 Iterable 的 iterator() 方法获取一个 Iterator,然后像操作普通 Iterator 一样进行遍历。更多关于 Iterator 的内容,请参阅我的 Java Iterator 教程。
以下是使用 Iterator 遍历 Iterable(本例中为 List)的示例:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
使用 forEach() 方法遍历 Iterable
第三种方式是调用 Iterable 的 forEach() 方法。该方法接收一个 Java Lambda 表达式 作为参数,该表达式会对 Iterable 中的每个元素执行一次。
示例如下:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.forEach((element) -> {
System.out.println(element);
});
Iterable 接口定义
Java 的 Iterable 接口包含三个方法,其中只有一个必须实现,其余两个提供了默认实现。接口定义如下:
public interface Iterable<T> {
Iterator<T> iterator();
Spliterator<T> spliterator();
void forEach(Consumer<? super T> action);
}
你必须实现的方法是 iterator(),它需要返回一个可用于遍历对象元素的 Iterator。当你在 for-each 循环中使用 Iterable 时,编译器会在背后自动调用此方法并生成相应的迭代代码。
Java 中 Iterable 的实现类
Iterable 接口(java.lang.Iterable)是 Java Collections API 的根接口之一,因此 Java 中有许多类实现了该接口,从而支持对其内部元素进行遍历。
此外,还有一些 Java 接口继承自 Iterable。任何实现这些子接口的类,也间接实现了 Iterable 接口。
例如:
Collection接口继承自IterableList和Set接口继承自Collection,因此也实现了Iterable
自定义 Iterable 实现
如果你想让自己的类支持 for-each 循环,你需要实现 Iterable 接口。具体实现方法在我的 Java 泛型教程 - 实现 Iterable 接口 中有详细说明。
这里仅给出一个简单的示例:
public class Persons implements Iterable<Person> {
private List<Person> persons = new ArrayList<Person>();
public Iterator<Person> iterator() {
return this.persons.iterator();
}
}
使用方式如下:
Persons persons = ... // 获取包含 Person 对象的 Persons 实例
for (Person person : persons) {
// 对 person 对象进行操作
}
获取 Spliterator
你可以通过 Iterable 的 spliterator() 方法获取一个 Spliterator 对象。本文不深入讲解 Spliterator,仅展示如何获取:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
Spliterator<String> spliterator = list.spliterator();
Iterable 的性能考量
如果你的代码需要在紧密循环中反复遍历集合(例如每秒数千次遍历一个 List),那么使用 for-each 循环会比使用传统的 for 循环稍慢:
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
}
原因在于:每次 for-each 循环都会调用 List.iterator() 方法,从而创建一个新的 Iterator 对象。如果每秒创建成千上万甚至上百万个对象,会产生一定的性能开销。
不过,对于大多数常规业务应用而言,这种性能差异完全可以忽略不计。只有在极高频率的循环场景下才需考虑。