第56章 Java Iterator迭代器
面包屑导航 章节标题 学习目标
学习目标
- 掌握Iterator接口的基本使用方法和核心概念
- 学会使用Iterator进行安全的集合遍历和元素删除
- 理解ListIterator的双向遍历和修改功能
- 掌握增强for循环的使用和限制
- 了解不同遍历方式的性能特点和适用场景
- 学会选择最适合的集合遍历方法
Iterator概述
Iterator概述
Iterator(迭代器)是Java集合框架中用于遍历集合元素的标准接口。它提供了一种统一的方式来访问集合中的元素,而不需要了解集合的内部结构。Iterator模式是设计模式中的一种,它将遍历逻辑从集合类中分离出来,使得遍历操作更加灵活和安全。
Iterator的核心优势统一接口:为所有集合提供统一的遍历方式安全删除:支持在遍历过程中安全删除元素fail-fast机制:检测并发修改,抛出ConcurrentModificationException内存效率:不需要额外的索引变量,适合大型集合
Iterator类型
Iterator类型详解
Iterator接口
boolean hasNext() // 检查是否有下一个元素
E next() // 返回下一个元素
void remove() // 删除当前元素(可选)
- 单向遍历(只能向前)
- 适用于所有Collection子类
- 支持安全删除操作
- fail-fast机制保证线程安全
ListIterator接口
boolean hasPrevious() // 检查是否有前一个元素
E previous() // 返回前一个元素
int nextIndex() // 返回下一个元素索引
int previousIndex() // 返回前一个元素索引
void set(E e) // 修改当前元素
void add(E e) // 在当前位置添加元素
- 双向遍历(前进和后退)
- 仅适用于List集合
- 支持添加、修改、删除操作
- 提供索引信息
增强for循环
for (ElementType element : collection) {
// 处理元素
}
- 基于Iterator实现的语法糖
- 代码简洁易读
- 适用于只读遍历
- 编译时自动转换为Iterator
Iterator基础使用
Iterator基础使用
基本遍历示例
import java.util.*;
public class IteratorBasicExample {
public static void main(String[] args) {
// 创建ArrayList集合
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橙子");
fruits.add("葡萄");
System.out.println("原始集合: " + fruits);
// 使用Iterator遍历集合
System.out.println("\n使用Iterator遍历:");
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println("- " + fruit);
}
}
}
安全删除操作
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
numbers.add(i);
}
System.out.println("删除前: " + numbers);
// 使用Iterator安全删除偶数
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
if (number % 2 == 0) {
iterator.remove(); // 安全删除当前元素
System.out.println("删除了: " + number);
}
}
System.out.println("删除后: " + numbers);
避免ConcurrentModificationException在遍历集合时,如果直接使用集合的remove()方法删除元素,会抛出ConcurrentModificationException异常。正确的做法是使用Iterator的remove()方法。// ❌ 错误做法 - 会抛出异常
for (String item : list) {
if (condition) {
list.remove(item); // 抛出ConcurrentModificationException
}
}
// ✅ 正确做法 - 使用Iterator
Iteratoriterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (condition) {
iterator.remove(); // 安全删除
}
}
ListIterator双向遍历
ListIterator双向遍历
ListIterator是Iterator的子接口,专门用于List集合,提供了双向遍历的能力。它不仅可以向前遍历,还可以向后遍历,并且支持在遍历过程中添加、修改和删除元素。
import java.util.*;
public class ListIteratorExample {
public static void main(String[] args) {
List<String> cities = new ArrayList<>();
cities.add("北京");
cities.add("上海");
cities.add("广州");
cities.add("深圳");
System.out.println("原始列表: " + cities);
// 正向遍历
System.out.println("\n正向遍历:");
ListIterator<String> listIterator = cities.listIterator();
while (listIterator.hasNext()) {
int index = listIterator.nextIndex();
String city = listIterator.next();
System.out.println("索引 " + index + ": " + city);
}
// 反向遍历
System.out.println("\n反向遍历:");
while (listIterator.hasPrevious()) {
int index = listIterator.previousIndex();
String city = listIterator.previous();
System.out.println("索引 " + index + ": " + city);
}
}
}
ListIterator修改操作
List<String> animals = new ArrayList<>();
animals.add("猫");
animals.add("狗");
animals.add("鸟");
System.out.println("初始列表: " + animals);
ListIterator<String> iterator = animals.listIterator();
// 在开头添加元素
iterator.add("鱼");
System.out.println("在开头添加'鱼': " + animals);
// 遍历并修改元素
while (iterator.hasNext()) {
String animal = iterator.next();
if (animal.equals("狗")) {
iterator.set("小狗"); // 修改当前元素
System.out.println("将'狗'修改为'小狗': " + animals);
} else if (animal.equals("鸟")) {
iterator.remove(); // 删除当前元素
System.out.println("删除'鸟': " + animals);
iterator.add("小鸟"); // 在当前位置添加新元素
System.out.println("添加'小鸟': " + animals);
}
}
System.out.println("最终列表: " + animals);
增强for循环
增强for循环详解
增强for循环(for-each循环)是Java 5引入的语法糖,它基于Iterator接口实现,提供了更简洁的集合遍历方式。编译器会自动将增强for循环转换为Iterator的使用。
// 遍历数组
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println("数字: " + number);
}
// 遍历List集合
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");
for (String fruit : fruits) {
System.out.println("水果: " + fruit);
}
// 遍历Set集合
Set<String> colors = new HashSet<>();
colors.add("红色");
colors.add("绿色");
colors.add("蓝色");
for (String color : colors) {
System.out.println("颜色: " + color);
}
增强for循环的限制
使用限制无法获取索引:增强for循环中无法直接获取元素的索引位置无法修改集合结构:不能在遍历中添加或删除元素无法修改基本类型数组元素:对于基本类型数组,修改循环变量不会影响原数组只能单向遍历:无法实现双向遍历或跳跃式遍历
// 限制1:无法获取索引
List<String> items = Arrays.asList("A", "B", "C");
// 解决方案:使用传统for循环或手动计数
int index = 0;
for (String item : items) {
System.out.println("索引 " + index + ": " + item);
index++;
}
// 限制2:无法修改基本类型数组元素
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
number = number * 2; // 只修改了局部变量,不影响原数组
}
System.out.println(Arrays.toString(numbers)); // 输出: [1, 2, 3, 4, 5]
// 正确的修改方式
for (int i = 0; i < numbers.length; i++) {
numbers[i] = numbers[i] * 2;
}
System.out.println(Arrays.toString(numbers)); // 输出: [2, 4, 6, 8, 10]
性能比较
遍历方式性能比较
不同的遍历方式在不同类型的集合上有着不同的性能表现。了解这些性能特点有助于我们在实际开发中选择最适合的遍历方法。
| 遍历方式 | 适用集合 | ArrayList性能 | LinkedList性能 | 可读性 | 功能特点 |
|---|---|---|---|---|---|
| 传统for循环 | List | 优秀 (O(1)) | 很差 (O(n²)) | 中等 | 支持索引,可修改 |
| Iterator | 所有集合 | 良好 (O(n)) | 良好 (O(n)) | 良好 | 安全删除,统一接口 |
| 增强for循环 | 所有集合 | 良好 (O(n)) | 良好 (O(n)) | 最佳 | 只读遍历,代码简洁 |
| Stream API | 所有集合 | 中等 | 中等 | 良好 | 函数式编程,链式操作 |
性能要点说明ArrayList:支持随机访问,传统for循环性能最佳LinkedList:链表结构,索引访问需要遍历,Iterator性能最佳HashSet/TreeSet:不支持索引访问,只能使用Iterator或增强for循环大数据集:性能差异更加明显,选择合适的遍历方式很重要
最佳实践
遍历最佳实践
选择指南
推荐做法
// 1. 只读遍历使用增强for循环
for (String item : collection) {
System.out.println(item);
}
// 2. 需要删除元素使用Iterator
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
if (shouldRemove(iterator.next())) {
iterator.remove();
}
}
// 3. ArrayList需要索引使用传统for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(i + ": " + list.get(i));
}
// 4. 使用removeIf方法(Java 8+)
list.removeIf(item -> shouldRemove(item));
- 优先使用增强for循环进行只读遍历
- 需要删除元素时使用Iterator
- ArrayList需要索引时使用传统for循环
- 利用Java 8的removeIf方法简化删除操作
避免的做法
// ❌ 在增强for循环中修改集合
for (String item : list) {
if (condition) {
list.remove(item); // 抛出异常
}
}
// ❌ LinkedList使用索引访问
for (int i = 0; i < linkedList.size(); i++) {
process(linkedList.get(i)); // O(n²)复杂度
}
// ❌ 重复获取集合大小
for (int i = 0; i < list.size(); i++) {
// list.size()在每次循环中都被调用
}
// ❌ 不必要的类型转换
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
String item = (String) iterator.next();
}
- 避免在增强for循环中修改集合结构
- 避免在LinkedList上使用索引访问
- 避免在循环中重复计算集合大小
- 避免不必要的类型转换,使用泛型
实际应用示例
实际应用示例
import java.util.*;
/**
* Iterator实际应用示例
* 演示在实际项目中如何正确使用Iterator
*/
public class IteratorPracticalExample {
public static void main(String[] args) {
// 创建任务列表
List<String> tasks = new ArrayList<>();
tasks.add("学习Java基础");
tasks.add("完成作业");
tasks.add("复习知识点");
tasks.add("准备考试");
System.out.println("任务列表: " + tasks);
// 使用ListIterator在每个任务前添加序号
addTaskNumbers(tasks);
// 使用Iterator安全删除已完成的任务
removeCompletedTasks(tasks, Arrays.asList("完成作业"));
// 使用增强for循环显示最终任务列表
displayTasks(tasks);
}
/**
* 使用ListIterator为任务添加序号
*/
private static void addTaskNumbers(List<String> tasks) {
ListIterator<String> iterator = tasks.listIterator();
int taskNumber = 1;
while (iterator.hasNext()) {
String task = iterator.next();
iterator.set(taskNumber + ". " + task);
taskNumber++;
}
System.out.println("\n添加序号后: " + tasks);
}
/**
* 使用Iterator安全删除已完成的任务
*/
private static void removeCompletedTasks(List<String> tasks,
List<String> completedTasks) {
Iterator<String> iterator = tasks.iterator();
while (iterator.hasNext()) {
String task = iterator.next();
// 检查任务是否已完成(忽略序号)
for (String completed : completedTasks) {
if (task.contains(completed)) {
iterator.remove();
System.out.println("删除已完成任务: " + task);
break;
}
}
}
}
/**
* 使用增强for循环显示任务列表
*/
private static void displayTasks(List<String> tasks) {
System.out.println("\n最终任务列表:");
for (String task : tasks) {
System.out.println(task);
}
}
}
💻 查看完整代码 - 在线IDE体验 常见问题
常见问题解答
Q: 什么时候使用Iterator而不是增强for循环?A: 当需要在遍历过程中删除元素时,或者需要更精细的遍历控制时,应该使用Iterator。增强for循环适合只读遍历。
Q: ListIterator和Iterator有什么区别?A: ListIterator支持双向遍历,可以向前和向后移动,还支持添加和修改元素操作,但仅适用于List集合。Iterator只支持单向遍历,适用于所有Collection。
Q: 为什么LinkedList不应该使用索引访问?A: LinkedList是链表结构,索引访问需要从头开始遍历到指定位置,时间复杂度为O(n)。在循环中使用索引访问会导致O(n²)的时间复杂度,性能很差。
Q: 增强for循环的内部实现是什么?A: 增强for循环在编译时会被转换为Iterator的使用。这就是为什么它适用于所有实现Iterable接口的集合,也是为什么在增强for循环中不能修改集合结构的原因。
本章小结
本章小结
- Iterator是Java集合框架中用于遍历集合元素的标准接口,提供了统一的遍历方式
- Iterator支持安全删除操作,避免了ConcurrentModificationException异常
- ListIterator是Iterator的子接口,支持双向遍历和更多的修改操作,仅适用于List集合
- 增强for循环是基于Iterator实现的语法糖,提供了简洁的只读遍历方式
- 不同遍历方式在不同集合类型上有不同的性能表现,需要根据场景选择
- ArrayList适合使用传统for循环进行索引访问,LinkedList适合使用Iterator遍历
- 在需要删除元素时应使用Iterator.remove()或removeIf()方法,避免直接修改集合
- 理解各种遍历方式的优缺点和适用场景,有助于编写高效的Java代码
导航链接 上一章:Queue接口 章节测试 下一章:Map接口 JavaScript