文章目录
- 0.个人感悟
- [1. 概念](#1. 概念)
- [2. 适配场景](#2. 适配场景)
-
- [2.1 适合的场景](#2.1 适合的场景)
- [2.2 常见场景举例](#2.2 常见场景举例)
- [3. 实现方法](#3. 实现方法)
-
- [3.1 实现思路](#3.1 实现思路)
- [3.2 UML类图](#3.2 UML类图)
- [3.3 代码示例](#3.3 代码示例)
-
- [3.3.1 类图](#3.3.1 类图)
- [3.3.2 代码跟踪](#3.3.2 代码跟踪)
- [4. 优缺点](#4. 优缺点)
-
- [4.1 优点](#4.1 优点)
- [4.2 缺点](#4.2 缺点)
0.个人感悟
- 迭代器我们并不陌生,java集合操作我们经常使用,只是有for-each、lambda语法糖后我们很少直接使用迭代器,从而忽略其中的原理和思想
- 迭代器模式很能体现解耦的一些思路:职责分离,聚合的数据和迭代操作分离;面向接口,聚合、迭代器都定义成接口;封装,只关注统一访问,不关注内部实现
- java集合是经典的设计,大家感兴趣可以了解下。我也计划后面专门总结jdk代码实现
1. 概念
英文定义 (《设计模式:可复用面向对象软件的基础》)
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
中文翻译
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
理解
- 分离了集合对象的遍历行为,抽象出一个迭代器来负责遍历
- 简化了聚合对象的接口,客户端只需要知道迭代器接口
- 可以在不修改聚合对象的前提下,增加新的遍历方式
- 为不同的聚合结构提供统一的遍历接口
2. 适配场景
2.1 适合的场景
- 遍历复杂数据结构:需要遍历复杂聚合对象,且不希望暴露其内部结构
- 统一遍历接口:为不同的聚合结构提供统一的遍历方式
- 支持多种遍历方式:聚合对象需要支持多种遍历方式(正序、逆序、按条件过滤等)
2.2 常见场景举例
- Java集合框架:List、Set、Map等集合的迭代器
- 数据库查询结果集:遍历数据库查询返回的多条记录
- 文件系统遍历:遍历目录树中的文件
- 社交网络关系遍历:遍历用户的好友、关注者等关系
- 菜单系统:遍历餐厅菜单中的菜品项
- XML/JSON解析:遍历解析树中的节点
3. 实现方法
3.1 实现思路
- 定义迭代器接口 :声明遍历集合元素所需的方法,如
hasNext()、next()等 - 实现具体迭代器:为特定聚合对象实现具体的迭代器,管理遍历过程中的当前位置
- 定义聚合接口 :声明创建迭代器对象的方法,通常为
iterator()或createIterator() - 实现具体聚合类:实现聚合接口,返回与自身对应的具体迭代器实例,通常持有集合
- 客户端使用迭代器:客户端通过迭代器接口遍历聚合对象,不直接操作聚合内部结构
3.2 UML类图

角色说明:
- Iterator(迭代器接口):定义访问和遍历元素的接口
- ConcreteIterator(具体迭代器):实现迭代器接口,记录遍历中的当前位置
- Aggregate(聚合接口):定义创建迭代器对象的接口
- ConcreteAggregate(具体聚合):实现聚合接口,返回具体迭代器的实例
3.3 代码示例
珠玉在前,就来看看ArraayList的相关实现
3.3.1 类图
跟踪代码,不然发现,类图:

3.3.2 代码跟踪
迭代器接口: 定义了标准的迭代器操作
java
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
聚合接口:
java
public interface Iterable<T> {
Iterator<T> iterator();
}
具体聚合和具体迭代器:ArrayList:实现,我们知道它的底层是Object[]; 具体迭代器是一个内部类Itr 。简化版代码如下
java
public class ArrayList<E> implements Iterable<E> {
// object数组
private Object[] elementData;
private int size;
// ... ArrayList的其他方法
// 实现Iterable接口,返回迭代器
@Override
public Iterator<E> iterator() {
return new Itr();
}
// 具体迭代器(内部类)
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个返回元素的索引
Itr() {}
@Override
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
@Override
public E next() {
if (cursor >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
E nextElement = (E) elementData[cursor];
lastRet = cursor;
cursor++;
return nextElement;
}
@Override
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
}
}
}
客户端使用:
java
public class Client {
static void main() {
// main
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
4. 优缺点
4.1 优点
- 符合单一职责原则:将遍历行为从聚合对象中分离,使聚合对象只关注数据存储
- 符合开闭原则:可以增加新的聚合类和迭代器类而无需修改现有代码
- 支持并行遍历:可以在同一个聚合对象上同时进行多个遍历
- 简化客户端代码:客户端使用统一的接口遍历不同的聚合结构
- 提高复用性:迭代器可以在多个地方复用,不需要重复编写遍历代码
4.2 缺点
- 增加系统复杂性:对于简单集合,直接遍历可能更简单
- 可能降低性能:迭代器需要维护遍历状态,比直接索引访问稍慢
- 访问限制:迭代器通常只提供顺序访问,不支持随机访问
- 并发修改问题:在迭代过程中修改集合可能导致异常
参考: