Java设计模式-迭代器模式
模式概述
迭代器模式简介
核心思想:将集合对象的遍历逻辑与集合本身的实现解耦,通过定义一个统一的迭代器接口,封装对集合元素的访问方式,使得客户端可以通过相同的方式遍历不同类型的集合,无需关心集合的内部结构。
模式类型:行为型设计模式(关注对象间的交互与职责分配)。
作用:
- 解耦遍历逻辑与集合实现:集合的内部结构(如数组、链表、树)变化时,只需修改具体迭代器,客户端遍历代码无需调整;
- 支持多态遍历:不同集合(如
List
、Set
)可通过同一迭代器接口实现遍历,客户端代码统一; - 简化复杂集合遍历:对于嵌套集合(如树形结构)或需要自定义遍历规则(如过滤、排序)的场景,迭代器可封装复杂逻辑。
典型应用场景:
- 集合框架遍历(如Java
Collection
/List
/Set
的Iterator
); - 数据库查询结果集遍历(如JDBC
ResultSet
); - 文件系统目录遍历(如遍历文件夹下的所有文件);
- 自定义复杂数据结构遍历(如图结构、树结构的深度优先/广度优先遍历)。
我认为:迭代器模式是“集合的万能钥匙”,用统一接口打开不同集合的遍历之门,让客户端优雅地“按顺序访问”而不必操心“门后结构”。
课程目标
- 理解迭代器模式的核心思想和经典应用场景
- 识别应用场景,使用迭代器模式解决功能要求
- 了解迭代器模式的优缺点
核心组件
角色-职责表
角色 | 职责 | 示例类名 |
---|---|---|
抽象迭代器(Iterator) | 定义遍历集合元素的通用接口(如hasNext() 、next() 、remove() ) |
Iterator (Java内置接口) |
具体迭代器(Concrete Iterator) | 实现抽象迭代器接口,持有集合引用并记录当前遍历位置,负责具体遍历逻辑 | ListIterator (列表迭代器) |
抽象容器(Aggregate) | 定义创建迭代器的方法(如iterator() ),声明集合的基础操作 |
Collection (Java集合接口) |
具体容器(Concrete Aggregate) | 实现抽象容器的创建迭代器方法,返回对应具体迭代器实例 | ArrayList (数组列表) |
类图
下面是一个简化的类图表示,展示了迭代器模式中的主要角色及其交互方式:
传统实现 VS 迭代器模式
案例需求
案例背景:实现一个支持多种遍历方式的“学生成绩册”系统,成绩册包含学生姓名列表,需要支持正向遍历(从第一个到最后一个)和反向遍历(从最后一个到第一个)。
传统实现(痛点版)
代码实现:
// 传统成绩册(未使用迭代器)
class GradeBook {
private List<String> students = new ArrayList<>();
public void addStudent(String name) {
students.add(name);
}
// 正向遍历(硬编码)
public void traverseForward() {
for (int i = 0; i < students.size(); i++) {
System.out.print(students.get(i) + " ");
}
System.out.println();
}
// 反向遍历(硬编码)
public void traverseBackward() {
for (int i = students.size() - 1; i >= 0; i--) {
System.out.print(students.get(i) + " ");
}
System.out.println();
}
}
// 客户端使用
public class TraditionalDemo {
public static void main(String[] args) {
GradeBook book = new GradeBook();
book.addStudent("张三");
book.addStudent("李四");
book.addStudent("王五");
System.out.println("正向遍历:");
book.traverseForward(); // 输出:张三 李四 王五
System.out.println("反向遍历:");
book.traverseBackward(); // 输出:王五 李四 张三
}
}
痛点总结:
- 紧耦合:每增加一种遍历方式(如按性别过滤遍历),需修改
GradeBook
类并新增遍历方法,违反“开闭原则”; - 遍历逻辑分散:遍历代码嵌入集合类内部,客户端需了解不同遍历方法的差异;
- 扩展性差:若成绩册的底层存储从
List
改为Set
(如HashSet
),正向/反向遍历方法可能失效(Set
无索引)。
迭代器模式 实现(优雅版)
代码实现:
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
// 抽象迭代器:定义遍历接口
interface StudentIterator extends Iterator<String> {
// 可选:扩展反向遍历方法(非必须,视需求)
boolean hasPrevious();
String previous();
}
// 具体容器:成绩册(持有学生列表)
class GradeBook implements Iterable<String> {
private List<String> students = new ArrayList<>();
public void addStudent(String name) {
students.add(name);
}
// 实现Iterable接口,返回具体迭代器
@Override
public Iterator<String> iterator() {
return new ForwardStudentIterator(students);
}
// 可选:提供反向迭代器工厂方法
public StudentIterator reverseIterator() {
return new ReverseStudentIterator(students);
}
}
// 具体迭代器:正向遍历
class ForwardStudentIterator implements StudentIterator {
private final List<String> students;
private int cursor = 0; // 当前遍历位置
public ForwardStudentIterator(List<String> students) {
this.students = students;
}
@Override
public boolean hasNext() {
return cursor < students.size();
}
@Override
public String next() {
if (!hasNext()) throw new IllegalStateException("无下一个元素");
return students.get(cursor++);
}
@Override
public boolean hasPrevious() {
return cursor > 0;
}
@Override
public String previous() {
if (!hasPrevious()) throw new IllegalStateException("无上一个元素");
return students.get(--cursor);
}
}
// 具体迭代器:反向遍历
class ReverseStudentIterator implements StudentIterator {
private final List<String> students;
private int cursor; // 当前遍历位置(初始化为最后一个元素)
public ReverseStudentIterator(List<String> students) {
this.students = students;
this.cursor = students.size() - 1;
}
@Override
public boolean hasNext() {
return cursor >= 0;
}
@Override
public String next() {
if (!hasNext()) throw new IllegalStateException("无下一个元素");
return students.get(cursor--);
}
@Override
public boolean hasPrevious() {
return cursor < students.size() - 1;
}
@Override
public String previous() {
if (!hasPrevious()) throw new IllegalStateException("无上一个元素");
return students.get(++cursor);
}
}
// 客户端使用
public class IteratorDemo {
public static void main(String[] args) {
GradeBook book = new GradeBook();
book.addStudent("张三");
book.addStudent("李四");
book.addStudent("王五");
System.out.println("正向遍历(默认迭代器):");
Iterator<String> forwardIterator = book.iterator();
while (forwardIterator.hasNext()) {
System.out.print(forwardIterator.next() + " "); // 输出:张三 李四 王五
}
System.out.println("
反向遍历(自定义迭代器):");
StudentIterator reverseIterator = book.reverseIterator();
while (reverseIterator.hasNext()) {
System.out.print(reverseIterator.next() + " "); // 输出:王五 李四 张三
}
// 扩展:新增“按奇偶位置过滤遍历”只需新增一个具体迭代器,无需修改GradeBook
}
}
优势:
- 解耦遍历逻辑:遍历方式(正向、反向、过滤等)由具体迭代器实现,成绩册(
GradeBook
)无需关心,符合“单一职责原则”; - 扩展性强:新增遍历方式只需添加新的迭代器类,无需修改现有代码,符合“开闭原则”;
- 统一接口:客户端通过
Iterator
接口遍历,无需了解集合底层是List
还是Set
,提高代码通用性。
局限:
- 额外复杂度:简单遍历场景(如仅需正向遍历)下,迭代器模式会增加类和接口数量,可能“过度设计”;
- 学习成本:客户端需熟悉迭代器接口(如
hasNext()
、next()
),但Java等语言已内置Iterator
,降低了学习门槛。
模式变体
- 外部迭代器 vs 内部迭代器:
- 外部迭代器:客户端显式控制遍历(如调用
next()
),灵活但需手动管理状态(如Java的Iterator
); - 内部迭代器:迭代器内部控制遍历(如Java 8的
forEach()
或Stream API),客户端只需定义操作逻辑(如student -> System.out.print(student + " ")
)。
- 外部迭代器:客户端显式控制遍历(如调用
- 过滤迭代器:在遍历时跳过不符合条件的元素(如只遍历分数大于60分的学生),通过装饰器模式扩展具体迭代器实现。
- 组合迭代器:遍历嵌套集合(如树形结构),递归调用子节点的迭代器(如
TreeSet
的遍历)。
最佳实践
建议 | 理由 |
---|---|
优先使用语言内置迭代器 | 如Java的Iterator 或Spliterator ,避免重复造轮子,利用标准库的健壮性。 |
避免在迭代中修改集合 | 迭代过程中直接修改集合(如添加/删除元素)可能导致ConcurrentModificationException (快速失败机制)。 |
自定义迭代器需实现Iterable |
若需支持增强型for 循环(for-each ),需让具体容器实现Iterable 接口并返回迭代器。 |
明确迭代器的线程安全性 | 若集合可能被多线程访问,需使用线程安全的迭代器(如CopyOnWriteArrayList 的迭代器)。 |
一句话总结
迭代器模式通过统一遍历接口解耦集合实现与遍历逻辑,使客户端能以一致的方式访问不同集合的元素,是提升代码灵活性和可维护性的经典方案。
如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊