Iterator 接口位于 java.util 包中,表示一个能够在 Java 对象集合中一次遍历一个对象的对象,还是 Java 中最古老的对象集合迭代机制之一(Enumerator 比 Iterator 还要古老)。
Iterator 接口提供了一种标准的方法来遍历集合中的元素,而无需关心集合的具体实现方式。使用它可以在不暴露集合内部结构的情况下,对集合中的元素进行访问和操作。
注意,要使用 Iterator,必须从要遍历的集合对象中获取一个 Iterator 实例。获取的 Iterator 会跟踪底层集合中的元素,以确保遍历所有元素。如果在遍历指向的底层集合时修改了该集合(如删除了元素),迭代器通常会检测到,并在下次尝试从迭代器中获取下一个元素时抛出异常。
Iterator 接口相当简单,核心方法如下:
hasNext():检查集合中是否还有下一个元素。如果有,返回 true;否则返回 false。
next():返回集合中的下一个元素,并将迭代器的位置向后移动一位。如果没有下一个元素,会抛出 NoSuchElementException 异常。
remove():移除迭代器最后返回的元素。该方法只能在每次调用 next() 方法之后调用一次,如果违反此规则,会抛出 IllegalStateException 异常。
forEachRemaining():对迭代器中的所有剩余元素进行迭代,并调用 Lambda 表达式,将每个剩余元素作为参数传递给该 Lambda 表达式。
这些方法中的每一个都将在以下部分中详细介绍。
Java 集合 Collection 接口包含一个名为 iterator() 的方法。通过调用 iterator() 方法,可以从给定的集合中获得一个迭代器。
你还可以从许多 Java 集合数据结构(如 List、Set、Map、Queue、Deque 或 Map)中获取迭代器。
下面是几个从各种 Java 集合类型中获取 Java 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"); // 从 List 获取迭代器 Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { // 遍历迭代器数据 System.out.println(iterator.next()); } System.out.println("======================="); Set<String> set = new HashSet<>(); set.add("one"); set.add("two"); set.add("three"); // 从 Set 获取迭代器 Iterator<String> iterator2 = set.iterator(); while(iterator2.hasNext()) { // 遍历迭代器数据 System.out.println(iterator2.next()); } } }
运行结果:
one two three ======================= one two three
你可以使用 while 循环遍历 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 val = iterator.next(); System.out.println(val); } } }
运行结果:
one two three
在上述 Java 示例中,有两个方法需要注意:
第一个方法是 hasNext() 方法,如果迭代器包含更多元素,该方法将返回 true,否则,返回 false。
第二个方法是 next() 方法,next() 方法返回迭代器正在迭代的集合的下一个元素。
注意:Iterator 中所含元素的遍历顺序取决于提供 Iterator 的对象。例如,从 List 获取的迭代器将按照 List 内部存储元素的相同顺序遍历 List 中的元素。而从集合获取的迭代器则不能保证集合中元素的确切迭代顺序。
某些集合不允许在通过迭代器迭代时修改集合。在这种情况下,下一次调用迭代器 next() 方法时,就会出现并发修改异常(ConcurrentModificationException)。下面的示例在执行时就会产生并发修改异常:
import java.util.*; public class Demo { public static void main(String[] args) { 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"); // 修改集合,添加新元素 } } } }
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049) at Demo.main(Demo.java:13)
注意,抛出 ConcurrentModificationException 的原因是,如果在通过迭代器迭代时修改了集合,迭代器就会与集合不同步。
Iterator 接口有一个 remove() 方法,可让你从底层集合中删除 next() 刚刚返回的元素。调用 remove() 不会引发并发修改异常(ConcurrentModificationException)。
下面是一个在 Iterator 迭代期间从集合中移除元素的示例:
import java.util.*; public class Demo { public static void main(String[] args) { 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(); // 移除元素 } } System.out.println(Arrays.toString(list.toArray())); // 输出:[123, 789] } }
Iterator 的 forEachRemaining() 方法可在内部遍历 Iterator 中剩余的所有元素,并为每个元素调用作为 forEachRemaining() 参数的 Lambda 表达式。
下面是一个使用 Iterator 的 forEachRemaining() 方法的示例:
import java.util.*; public class Demo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Jane"); list.add("Heidi"); list.add("Hannah"); Iterator<String> iterator = list.iterator(); // Lambda 表达式,迭代集合元素 iterator.forEachRemaining((element) -> { System.out.println(element); }); } }
运行结果:
Jane Heidi Hannah
ListIterator 是 Java 集合框架中的一个接口,继承自 Iterator 接口,专门用于遍历 List 集合。它在 Iterator 的基础上增加了双向遍历、修改元素和获取索引位置等功能,更适合处理列表类型的集合。
ListIterator 接口的主要方法如下:
boolean hasNext():判断是否有下一个元素(正向遍历)
E next():返回下一个元素,并将迭代器指针后移
boolean hasPrevious():判断是否有前一个元素(反向遍历)
E previous():返回前一个元素,并将迭代器指针前移
int nextIndex():返回下一个元素的索引
int previousIndex():返回前一个元素的索引
void remove():删除 next() 或 previous() 方法最后返回的元素(继承自 Iterator,但实现更完善)
void set(E e):用指定元素替换 next() 或 previous() 方法最后返回的元素
void add(E e):在当前迭代位置插入指定元素
简单示例:
import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class ListIteratorExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); ListIterator<String> iterator = list.listIterator(); // 正向遍历 System.out.println("正向遍历:"); while (iterator.hasNext()) { System.out.println("索引 " + iterator.nextIndex() + ": " + iterator.next()); } // 反向遍历 System.out.println("\n反向遍历:"); while (iterator.hasPrevious()) { System.out.println("索引 " + iterator.previousIndex() + ": " + iterator.previous()); } // 添加元素 iterator.add("D"); System.out.println("\n添加元素后: " + list); // 修改元素 iterator.next(); // 移动到元素"A" iterator.set("A+"); System.out.println("修改元素后: " + list); } }
运行结果:
正向遍历: 索引 0: A 索引 1: B 索引 2: C 反向遍历: 索引 2: C 索引 1: B 索引 0: A 添加元素后: [D, A, B, C] 修改元素后: [D, A+, B, C]
如你所见,这个示例首先通过所有元素正向遍历 ListIterator,然后再次反向遍历所有元素回到第一个元素。
如果你需要自定义迭代器,可以自己实现 Iterator 接口,创建一个可以遍历集合元素的迭代器。下面展示 Iterator 接口的一个简单的自定义实现,可让你了解自己实现 Iterator 接口的过程。
示例如下:
import java.util.ArrayList; import java.util.Iterator; 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() ); } } } /** * 自定义的迭代器类,实现了 Iterator 接口 * 用于遍历 List 类型的集合 * @param <T> 泛型参数,表示集合中元素的类型 */ class ListIterator <T> implements Iterator<T> { // 存储要遍历的源集合 private List<T> source = null; // 当前迭代的索引位置,初始值为0(指向第一个元素) private int index = 0; /** * 构造方法,初始化迭代器 * @param source 要遍历的List集合 */ public ListIterator(List<T> source){ this.source = source; } /** * 判断是否还有下一个元素 * @return 如果存在下一个元素则返回true,否则返回false */ @Override public boolean hasNext() { // 当当前索引小于集合大小,说明还有元素未遍历 return this.index < this.source.size(); } /** * 获取下一个元素,并将索引位置后移 * @return 下一个元素 */ @Override public T next() { // 获取当前索引位置的元素,然后将索引加1 return this.source.get(this.index++); } }
运行结果:
one two three