Iterable 接口表示一个可迭代的对象集合,其核心含义是 “支持迭代操作”。这意味着,任何实现了 Iterable 接口的类,都具备被遍历的能力,能够通过迭代器(Iterator)依次访问其包含的元素,这也是集合类支持 for-each 循环的基础。
迭代 Iterable 元素的第一种方法是使用 for-each 循环。下面示例展示了如何通过 for-each 循环遍历 List 的元素。由于 List 接口扩展了 Collection 接口,而 Collection 接口扩展了 Iterable 接口,因此 List 对象可以与 for-each 循环一起使用。
示例代码:
import java.util.*; public class Demo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); for( String element : list ){ System.out.println( element.toString() ); } } }
运行结果:
one two three
此示例首先创建一个新的列表并向其中添加 3 个元素。然后,它使用增强 for 循环来迭代列表的元素,并打印出每个元素的 toString() 值。
遍历 Iterable 元素的第二种方法是通过调用 Iterable 的 iterator() 方法从中获取一个 Iterator。然后,你可以像使用其他迭代器一样遍历该迭代器。例如:
import java.util.*; public class Demo { public static void main(String[] args) { 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 ); } } }
运行结果:
one two three
遍历 Iterable 元素的第三种方法是使用 forEach() 方法。forEach()方法将 lambda 表达式作为参数。迭代表中的每个元素都会调用一次这个 lambda 表达式。例如:
import java.util.*; public class Demo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); // 迭代 list.forEach( (element) -> { System.out.println( element ); }); } }
运行结果:
one two three
Iterable 接口有三个方法,其中只有一个需要实现,另外两个方法有默认实现。下面是 Iterable 接口的定义:
package java.lang; import java.util.Iterator; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; /** * 实现此接口的对象可以作为增强型for循环(有时称为"for-each循环")的目标。 * 它是Java集合框架中迭代操作的基础接口,提供了统一的元素遍历方式。 * * <p>任何实现了Iterable接口的类都可以被迭代,这使得客户端代码可以使用一致的方式 * 遍历不同类型的集合,而无需关心其内部实现细节。 * * @param <T> 迭代器返回的元素类型 * * @since 1.5 (Java 5引入) * @jls 14.14.2 增强型for语句规范 */ public interface Iterable<T> { /** * 返回一个用于遍历T类型元素的迭代器。 * 迭代器提供了hasNext()和next()等方法,支持对集合元素的顺序访问。 * * @return 用于遍历元素的Iterator实例 */ Iterator<T> iterator(); /** * 对Iterable中的每个元素执行给定的操作,直到所有元素都被处理或操作抛出异常为止。 * 操作按照迭代顺序执行(如果指定了迭代顺序)。操作抛出的异常会直接传递给调用者。 * * <p>如果操作执行了修改底层元素源的副作用,此方法的行为是未指定的, * 除非覆盖此类指定了并发修改策略。 * * @implSpec (实现规范) * <p>默认实现的行为等同于: * <pre>{@code * for (T t : this) * action.accept(t); * }</pre> * * @param action 要对每个元素执行的操作 * @throws NullPointerException 如果指定的action为null * @since 1.8 (Java 8引入,支持函数式编程) */ default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); // 确保操作不为null,否则抛出异常 for (T t : this) { // 使用增强for循环遍历所有元素 action.accept(t); // 对每个元素执行消费操作 } } /** * 为当前Iterable描述的元素创建一个Spliterator(可分割迭代器)。 * Spliterator支持并行遍历,是Java 8中为了支持集合并行处理而引入的接口。 * * @implSpec (实现规范) * 默认实现从Iterable的迭代器创建一个"早期绑定"的spliterator。 * 该spliterator继承了Iterable迭代器的"快速失败"特性(当检测到并发修改时抛出异常)。 * * @implNote (实现注意事项) * 默认实现通常应该被重写。默认实现返回的spliterator具有较差的分割能力, * 未指定大小,并且不报告任何spliterator特性。实现类几乎总能提供更好的实现。 * * @return 用于遍历当前Iterable元素的Spliterator * @since 1.8 (Java 8引入,支持并行流操作) */ default Spliterator<T> spliterator() { // 创建一个未知大小的spliterator,使用当前Iterable的迭代器作为数据源 return Spliterators.spliteratorUnknownSize(iterator(), 0); } }
注意,你必须实现的方法名为 iterator()。该方法必须返回一个 Iterator,用于遍历实现 Iterable 接口的对象中的元素。获取迭代器的过程是在幕后进行的,所以你看不到这个过程。当您在 for-each 循环中使用 Iterable 时,Java 编译器会负责生成相关代码。
Iterable 接口是 Java 集合框架的根接口之一,Java 中有多个类实现了 Iterable 接口。因此,这些类可以迭代其内部元素。
还有几个 Java 接口扩展了 Iterable 接口。因此,实现了 Iterable 扩展接口的类也实现了 Iterable 接口。这些类也可以迭代其元素。
Collection 接口扩展了 Iterable,因此 Collection 的所有子类也实现了 Iterable 接口。例如,Java 的 List 和 Set 接口都扩展了 Collection 接口,从而也扩展了 Iterable 接口。如下图:
要实现 Iterable 接口以便使用 for-each 循环,只需完成两个核心步骤:
(1)实现 Iterable 接口并提供 iterator() 方法。
(2)实现对应的 Iterator 接口来定义遍历逻辑。
以下是一个完整示例,展示如何自定义一个可迭代的集合类:
import java.util.Iterator; // 自定义可迭代的集合类 class MyCollection<T> implements Iterable<T> { private T[] elements; private int size; // 构造方法初始化集合 @SuppressWarnings("unchecked") public MyCollection(int capacity) { elements = (T[]) new Object[capacity]; size = 0; } // 添加元素方法 public void add(T element) { if (size < elements.length) { elements[size++] = element; } } // 实现Iterable接口的核心方法,返回迭代器 @Override public Iterator<T> iterator() { return new MyIterator(); } // 利用内部类实现 Iterator 接口 private class MyIterator implements Iterator<T> { private int currentIndex = 0; // 跟踪当前迭代位置 // 判断是否还有下一个元素 @Override public boolean hasNext() { return currentIndex < size; } // 获取下一个元素并移动指针 @Override public T next() { return elements[currentIndex++]; } } } public class CustomIterableExample { public static void main(String[] args) { // 创建自定义集合并添加元素 MyCollection<String> collection = new MyCollection<>(3); collection.add("Apple"); collection.add("Banana"); collection.add("Cherry"); // 使用for-each循环遍历(得益于实现了Iterable接口) for (String fruit : collection) { System.out.println(fruit); } } }
运行结果:
Apple Banana Cherry
注意,迭代器的状态(如 currentIndex)应该封装在 Iterator 实现中,而非 Iterable 类中,这样支持同时创建多个独立的迭代器。如果需要支持移除元素,可以在 Iterator 中重写 remove() 方法,Java 8+ 中还可以选择性重写 forEach() 方法,提供更灵活的遍历方式。
可以通过 spliterator() 方法从 Iterable 获取 Spliterator。例如:
import java.util.ArrayList; import java.util.List; import java.util.Spliterator; public class SpliteratorDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); list.add("four"); list.add("five"); // 获取列表的Spliterator Spliterator<String> spliterator = list.spliterator(); System.out.println("=== 1. 遍历所有元素 ==="); // 使用forEachRemaining遍历元素 spliterator.forEachRemaining(element -> System.out.println(element)); // 重新获取Spliterator spliterator = list.spliterator(); System.out.println("\n=== 2. 尝试分割Spliterator ==="); // 尝试分割为两个Spliterator Spliterator<String> secondSpliterator = spliterator.trySplit(); if (secondSpliterator != null) { System.out.println("--- 第一个部分的元素 ---"); spliterator.forEachRemaining(System.out::println); System.out.println("--- 第二个部分的元素 ---"); secondSpliterator.forEachRemaining(System.out::println); } // 重新获取Spliterator spliterator = list.spliterator(); System.out.println("\n=== 3. 特性与估计大小 ==="); // 查看Spliterator的特性 int characteristics = spliterator.characteristics(); System.out.println("是否有序: " + ((characteristics & Spliterator.ORDERED) != 0)); System.out.println("是否有大小: " + ((characteristics & Spliterator.SIZED) != 0)); System.out.println("估计元素数量: " + spliterator.estimateSize()); System.out.println("\n=== 4. 逐个处理元素 ==="); // 逐个处理元素 while (spliterator.tryAdvance(element -> System.out.println("处理元素: " + element))) { // tryAdvance返回true表示还有元素可处理 } } }
运行结果:
=== 1. 遍历所有元素 === one two three four five === 2. 尝试分割Spliterator === --- 第一个部分的元素 --- three four five --- 第二个部分的元素 --- one two === 3. 特性与估计大小 === 是否有序: true 是否有大小: true 估计元素数量: 5 === 4. 逐个处理元素 === 处理元素: one 处理元素: two 处理元素: three 处理元素: four 处理元素: five
如果你编写的代码需要在一个循环中多次迭代一个集合,比方说每秒迭代一个 List 上千次,那么通过 for-each 进行循环迭代 List 要比通过标准 for 循环迭代 List 慢,示例代码:
import java.util.ArrayList; import java.util.List; public class IterablePerformance { public static void main(String[] args) { // 初始化一个大小为 1 万的 list List<String> list = new ArrayList<>(); for(int i = 0; i < 10000; i++) { list.add(String.valueOf(System.currentTimeMillis())); } // 通过传统方式遍历10000次 long s1 = System.currentTimeMillis(); for(int n = 0; n < 10000; n++) { for(int i = 0; i < list.size(); i++) { String val = list.get(i); } } System.out.println("传统方式耗时:" + (System.currentTimeMillis() - s1)); // 通过for-each方式遍历10000次 long s2 = System.currentTimeMillis(); for(int n = 0; n < 10000; n++) { for(String str : list) { // } } System.out.println("for-each方式耗时:" + (System.currentTimeMillis() - s2)); } }
运行结果:
传统方式耗时:134 for-each方式耗时:262
注意,for-each 循环速度较慢的原因是,每次迭代都会调用 iterator() 方法,从而创建一个新的 Iterator 对象。与使用标准 for 循环迭代 List 相比,每秒创建一个新对象数千次,甚至数百万次,确实会对性能造成一定影响。对于偶尔迭代集合的大多数标准业务应用程序来说,这种性能差异无关紧要。只有在每秒执行数千次的非常紧凑的循环中才会出现这种情况。