文章目录
-
- 前言
- [1. 迭代器模式是什么?](#1. 迭代器模式是什么?)
- [2. 迭代器模式解决什么问题?](#2. 迭代器模式解决什么问题?)
- [3. 核心结构](#3. 核心结构)
-
- [3.1 Iterator(抽象迭代器)](#3.1 Iterator(抽象迭代器))
- [3.2 ConcreteIterator(具体迭代器)](#3.2 ConcreteIterator(具体迭代器))
- [3.3 Aggregate(抽象集合)](#3.3 Aggregate(抽象集合))
- [3.4 ConcreteAggregate(具体集合)](#3.4 ConcreteAggregate(具体集合))
- [4. 实现思路](#4. 实现思路)
- [5. 示例](#5. 示例)
-
- [5.1 Iterator:抽象迭代器](#5.1 Iterator:抽象迭代器)
- [5.2 Aggregate:抽象集合](#5.2 Aggregate:抽象集合)
- [5.3 ConcreteAggregate:具体集合------书架](#5.3 ConcreteAggregate:具体集合——书架)
- [5.4 ConcreteIterator:具体迭代器](#5.4 ConcreteIterator:具体迭代器)
- [5.5 Client:客户端遍历](#5.5 Client:客户端遍历)
- [6. 优缺点](#6. 优缺点)
-
- [6.1 优点](#6.1 优点)
- [6.2 缺点](#6.2 缺点)
- [7. 和其他模式怎么区分?](#7. 和其他模式怎么区分?)
-
- [7.1 迭代器 vs 访问者](#7.1 迭代器 vs 访问者)
- [7.2 迭代器 vs 组合模式](#7.2 迭代器 vs 组合模式)
- [7.3 迭代器 vs 策略模式](#7.3 迭代器 vs 策略模式)
- [8. 适用场景](#8. 适用场景)
- [9. Java 中的实际应用](#9. Java 中的实际应用)
- [10. 总结](#10. 总结)
前言
在日常开发中,我们经常需要遍历一个集合里的所有元素,比如:
- 遍历一棵树的所有节点(前序/中序/后序)
- 遍历一个自定义列表、链表、图结构
- 遍历数据库查询结果集
- 遍历文件目录下的所有文件
这些场景有一个共同点:集合的内部结构各不相同,但客户端只想一个一个地拿到元素,不想关心底层是数组、链表还是树。
迭代器模式(Iterator Pattern)要解决的核心就是:
提供一种统一的方式,顺序访问集合中的每个元素,而不暴露集合的内部表示。
1. 迭代器模式是什么?
迭代器模式是一种行为型设计模式,它将"遍历逻辑"从集合本身中抽离出来,封装到一个独立的迭代器对象中,从而做到:
- 客户端用统一的方式(
hasNext()/next())遍历任何集合 - 集合的内部结构可以自由变化,不影响遍历代码
- 同一个集合可以同时存在多个独立的遍历过程
2. 迭代器模式解决什么问题?
- 集合内部结构复杂(数组、链表、树、图......),不希望把遍历细节暴露给调用方
- 希望对不同类型的集合提供统一的遍历接口
- 需要支持多种遍历方式(正序、倒序、过滤遍历等),且不想在集合类里堆一堆遍历方法
- 希望同一集合能被多个迭代器同时遍历,互不干扰
如果发现客户端代码里到处写
for (int i = 0; i < list.size(); i++)这种依赖集合内部索引的遍历,而且集合结构还可能换,就很适合迭代器模式。
3. 核心结构
3.1 Iterator(抽象迭代器)
定义遍历集合所需的统一方法,例如 hasNext()、next()。
3.2 ConcreteIterator(具体迭代器)
实现迭代器接口,持有对具体集合的引用,维护当前遍历位置等状态。
3.3 Aggregate(抽象集合)
定义创建迭代器的方法,例如 createIterator()。
3.4 ConcreteAggregate(具体集合)
实现集合接口,返回与自身匹配的具体迭代器实例。
4. 实现思路
- 定义
Iterator接口(统一hasNext()+next()方法签名) - 定义
Aggregate接口(统一createIterator()方法) - 写
ConcreteAggregate(具体集合,存储元素) - 写
ConcreteIterator(具体迭代器,知道如何遍历对应集合) - 客户端只跟
Iterator和Aggregate接口交互,不关心集合内部结构
5. 示例
自定义书架集合,用迭代器遍历所有书籍
5.1 Iterator:抽象迭代器
java
public interface Iterator<T> {
boolean hasNext();
T next();
}
5.2 Aggregate:抽象集合
java
public interface Aggregate<T> {
Iterator<T> createIterator();
}
5.3 ConcreteAggregate:具体集合------书架
java
public class BookShelf implements Aggregate<String> {
private final String[] books;
private int count = 0;
public BookShelf(int capacity) {
this.books = new String[capacity];
}
public void addBook(String name) {
books[count++] = name;
}
public String getBookAt(int index) {
return books[index];
}
public int getSize() {
return count;
}
@Override
public Iterator<String> createIterator() {
return new BookShelfIterator(this);
}
}
5.4 ConcreteIterator:具体迭代器
java
public class BookShelfIterator implements Iterator<String> {
private final BookShelf bookShelf;
private int index = 0;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return index < bookShelf.getSize();
}
@Override
public String next() {
String book = bookShelf.getBookAt(index);
index++;
return book;
}
}
5.5 Client:客户端遍历
java
public class Client {
public static void main(String[] args) {
BookShelf shelf = new BookShelf(5);
shelf.addBook("设计模式");
shelf.addBook("重构");
shelf.addBook("代码整洁之道");
shelf.addBook("Effective Java");
Iterator<String> it = shelf.createIterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
会发现:
- 客户端完全不知道书架内部用的是数组------哪天换成
ArrayList或链表,遍历代码一行不用改 - 遍历逻辑被封装在
BookShelfIterator里,书架类本身保持干净 - 如果想加一个"倒序迭代器",只需新增一个
ReverseBookShelfIterator,不动现有代码
6. 优缺点
6.1 优点
- 统一遍历接口 :不管底层是数组、链表还是树,客户端用同一套
hasNext/next - 解耦集合与遍历:集合只管存储,迭代器只管遍历,职责清晰
- 支持多种遍历方式:同一集合可以提供正序、倒序、过滤等不同迭代器
- 符合开闭原则:新增遍历方式只需新增迭代器类,不改集合代码
6.2 缺点
- 类数量增加:每种集合通常需要配一个迭代器类
- 对于简单集合(如普通数组、
ArrayList),直接用 for-each 就够了,引入迭代器模式反而过度设计 - 迭代过程中如果集合被修改,可能引发并发问题(Java 的
ConcurrentModificationException就是典型例子)
7. 和其他模式怎么区分?
7.1 迭代器 vs 访问者
- 迭代器:关注"如何逐个访问元素",不关心对元素做什么操作
- 访问者:关注"对不同类型的元素执行不同操作",遍历本身可能借助迭代器完成
7.2 迭代器 vs 组合模式
- 组合模式:把对象组织成树形结构,统一叶子和容器的接口
- 迭代器:经常和组合模式搭配使用------组合模式定义结构,迭代器负责遍历这棵树
7.3 迭代器 vs 策略模式
- 策略模式:封装的是"算法/规则",运行时替换计算逻辑
- 迭代器模式:封装的是"遍历方式",运行时替换遍历逻辑
本质上迭代器可以看作一种特化的策略------"遍历策略"。
8. 适用场景
满足以下情况时,迭代器模式很合适:
- 集合内部结构复杂或可能变化,不希望暴露给客户端
- 需要为同一集合提供多种遍历方式
- 希望用统一接口遍历不同类型的集合
- 需要支持同时进行多个独立的遍历(每个迭代器维护自己的游标)
9. Java 中的实际应用
其实你每天都在用迭代器模式------Java 的 java.util.Iterator 和增强 for 循环就是它的标准实现:
java
List<String> list = List.of("A", "B", "C");
// 显式使用迭代器
java.util.Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// for-each 语法糖,底层就是迭代器
for (String s : list) {
System.out.println(s);
}
任何实现了 Iterable 接口的类都能用 for-each,这就是迭代器模式在语言层面的落地。
10. 总结
迭代器模式通过"抽象迭代器 + 具体迭代器 + 抽象集合 + 具体集合",将遍历逻辑从集合中解耦出来。客户端只需通过统一的
hasNext/next接口逐个获取元素,无需了解集合内部是数组、链表还是树,从而让集合结构和遍历方式各自独立变化。
