迭代器模式(长文讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言:为什么需要迭代器模式?
在软件开发中,遍历数据结构是开发者最频繁的操作之一。无论是遍历数组、链表,还是更复杂的自定义集合,如何高效且灵活地访问元素始终是一个核心问题。想象一个场景:你正在开发一个电商平台,需要遍历用户订单列表,但订单可能存储在数据库、内存对象或远程 API 中。此时,如果每次遍历都直接依赖具体存储方式,代码会变得臃肿且难以维护。
迭代器模式(Iterator Pattern)正是为了解决这一问题而诞生的面向对象设计模式。它如同一位专业导游,通过统一接口引导游客(程序)按顺序访问景点(数据元素),而无需关心景点的具体位置或分布方式。这种解耦设计让代码更加简洁、可扩展,并符合开放封闭原则。
核心概念:迭代器模式的组成要素
迭代器模式由四个关键角色构成,它们共同协作实现数据遍历的抽象化:
1. 迭代器接口(Iterator Interface)
定义访问和遍历元素的基本方法,通常包含以下功能:
hasNext()
: 判断是否还有可访问的元素next()
: 获取下一个元素- 可选的
remove()
或reset()
等辅助方法
// Java示例:迭代器接口定义
public interface Iterator<T> {
boolean hasNext();
T next();
}
2. 具体迭代器(Concrete Iterator)
实现迭代器接口,维护遍历过程中的状态(如当前索引位置),并实现具体的遍历逻辑。
3. 聚合接口(Aggregate Interface)
定义创建迭代器的工厂方法,例如:
public interface Aggregate {
Iterator createIterator();
}
4. 具体聚合(Concrete Aggregate)
实现聚合接口,提供实际的数据集合和迭代器实例。例如一个 BookShelf
类可以实现 Aggregate
接口,返回对应的 BookIterator
。
表格总结:迭代器模式角色职责
角色名称 | 职责说明 |
---|---|
迭代器接口 | 定义访问元素的标准方法 |
具体迭代器 | 实现遍历逻辑,维护遍历状态 |
聚合接口 | 提供创建迭代器的统一接口 |
具体聚合 | 管理实际数据集合,并返回对应的迭代器实例 |
具体实现:从传统遍历到模式应用
传统遍历方式的局限性
以遍历书籍列表为例,传统方式可能直接使用索引:
// 不使用迭代器模式的代码
Book[] books = new Book[10];
for(int i=0; i<books.length; i++) {
System.out.println(books[i].getTitle());
}
这种写法有三个缺点:
- 依赖数组的具体实现方式(如索引)
- 难以适应链表等非连续存储结构
- 无法灵活切换遍历方向(如反向遍历)
迭代器模式的分步实现
步骤1:定义迭代器接口
public interface BookIterator implements Iterator<Book> {
@Override
boolean hasNext();
@Override
Book next();
}
步骤2:创建具体迭代器
public class BookArrayIterator implements BookIterator {
private Book[] books;
private int index = 0;
public BookArrayIterator(Book[] books) {
this.books = books;
}
@Override
public boolean hasNext() {
return index < books.length;
}
@Override
public Book next() {
if (!hasNext()) throw new NoSuchElementException();
return books[index++];
}
}
步骤3:实现聚合接口
public interface BookAggregate {
BookIterator iterator();
}
步骤4:具体聚合类集成
public class BookShelf implements BookAggregate {
private Book[] books;
@Override
public BookIterator iterator() {
return new BookArrayIterator(books);
}
}
使用示例
BookShelf shelf = new BookShelf();
BookIterator iter = shelf.iterator();
while(iter.hasNext()) {
System.out.println(iter.next().getTitle());
}
扩展应用:迭代器模式的变体与最佳实践
1. 内部迭代器 vs 外部迭代器
- 外部迭代器(如Java
Iterator
):需要显式控制循环,适合需要灵活控制遍历过程的场景 - 内部迭代器(如Python
for...in
):通过回调函数自动遍历,代码更简洁但灵活性较低
class BookShelf:
def __iter__(self):
return BookIterator(self.books)
for book in BookShelf():
print(book.title)
2. 惰性求值与性能优化
在处理大数据时,可结合生成器实现惰性计算:
def book_generator(books):
for book in books:
if book.available:
yield book
shelf = BookShelf()
for available_book in book_generator(shelf.books):
print(available_book.title)
3. 多维度遍历支持
通过创建不同迭代器实现,可支持:
- 正向/反向遍历
- 过滤性遍历(如只遍历特定分类书籍)
- 分页式遍历
public class ReverseBookIterator implements BookIterator {
private Book[] books;
private int index;
public ReverseBookIterator(Book[] books) {
this.books = books;
this.index = books.length - 1;
}
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public Book next() {
return books[index--];
}
}
实际应用场景分析
场景1:自定义数据结构遍历
当需要遍历树形结构、图结构等复杂数据时,通过迭代器模式可轻松实现:
// 简化版树节点遍历迭代器
public class TreeIterator implements Iterator<Node> {
private Stack<Node> stack;
public TreeIterator(Node root) {
stack = new Stack<>();
pushAllLeft(root);
}
private void pushAllLeft(Node node) {
while(node != null) {
stack.push(node);
node = node.left;
}
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public Node next() {
Node node = stack.pop();
if(node.right != null) {
pushAllLeft(node.right);
}
return node;
}
}
场景2:数据库分页查询
在分页查询时,可封装为支持 hasNextPage()
和 nextPage()
的迭代器:
public class DatabasePageIterator implements Iterator<List<User>> {
private int currentPage = 0;
private final int pageSize;
private final Database database;
public DatabasePageIterator(Database database, int pageSize) {
this.database = database;
this.pageSize = pageSize;
}
@Override
public boolean hasNext() {
return currentPage * pageSize < database.getTotalCount();
}
@Override
public List<User> next() {
return database.fetchPage(currentPage++, pageSize);
}
}
场景3:组合模式遍历
在组合模式中,通过统一接口遍历组件集合:
public interface Component {
void accept(Visitor visitor);
}
public class Composite implements Component {
private List<Component> children = new ArrayList<>();
@Override
public void accept(Visitor visitor) {
for(Component child : children) {
child.accept(visitor);
}
visitor.visit(this);
}
}
常见问题与解决方案
问题1:迭代器状态不一致
当多个迭代器同时遍历同一集合时,可能出现状态混乱。解决方案:
- 使用线程安全的集合类型
- 禁止在遍历时修改集合(如Java的
ConcurrentModificationException
) - 提供
clone()
方法创建独立的迭代器实例
问题2:内存占用过高
对于大数据集合,避免一次性加载所有数据。解决方案:
- 使用惰性迭代器(如生成器)
- 分页式加载数据
- 结合数据库游标实现
问题3:遍历逻辑复杂度高
当需要复合遍历条件时,可以:
- 使用装饰器模式扩展迭代器功能
- 组合多个迭代器实现逻辑组合
- 通过策略模式选择不同的遍历策略
结论:迭代器模式的价值与实践建议
迭代器模式通过抽象数据访问方式,将遍历逻辑从业务代码中解耦,带来了以下核心价值:
- 解耦数据与遍历逻辑:客户端无需了解数据存储细节
- 统一访问接口:无论数据是数组、链表还是远程服务,均可通过统一接口访问
- 可扩展性:新增遍历方式时只需实现新迭代器
- 代码复用:遍历逻辑可复用在不同数据结构
对于开发者而言,建议:
- 在自定义数据结构时优先考虑迭代器支持
- 使用语言内置的迭代器功能(如Java的
Iterable
接口) - 结合设计模式组合使用(如与组合模式、观察者模式配合)
- 根据场景选择内部/外部迭代器实现
通过合理运用迭代器模式,我们不仅能写出更优雅的代码,还能为系统的可维护性和扩展性打下坚实基础。正如一位优秀的导游能带领游客发现景点的最佳路径,迭代器模式也为程序提供了访问数据的最优路径。