Java 设计模式・迭代器模式篇:从思想到代码实现
一、行为型模式
在面向对象的世界里,如何优雅地组织对象间的交互、分配职责,是每一位开发者都会反复思考的问题。直接硬编码交互逻辑固然简单,但当业务复杂度上升、对象协作关系变得错综复杂时,这种方式就会让代码变得僵化、难以扩展。
行为型设计模式正是为了解决这一痛点而诞生的一套思想体系。它们关注如何定义对象之间的通信方式和职责分配,通过命令、迭代、观察者、策略等手段,让对象间的协作更具灵活性、可复用性和可维护性。
在 Java 开发中,行为型模式主要包含以下 11 种经典实现:
- 模板方法模式 (Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Java 设计模式・模板方法模式篇:从思想到代码实现-CSDN博客
- 策略模式 (Strategy):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,让算法独立于使用它的客户而变化。Java 设计模式・策略模式篇:从思想到代码实现-CSDN博客
- 命令模式 (Command):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,支持可撤销操作。Java 设计模式・命令模式篇:从思想到代码实现-CSDN博客
- 责任链模式 (Chain of Responsibility):将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求,形成一条处理链。Java 设计模式・责任链模式篇:从思想到代码实现-CSDN博客
- 状态模式 (State):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。Java 设计模式・状态模式篇:从思想到代码实现-CSDN博客
- 观察者模式 (Observer):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。Java 设计模式・观察者模式篇:从思想到代码实现-CSDN博客
- 中介者模式 (Mediator):用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而降低耦合。Java 设计模式・中介者模式篇:从思想到代码实现-CSDN博客
- 迭代器模式 (Iterator):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
- 访问者模式 (Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。Java 设计模式・访问者模式篇:从思想到代码实现-CSDN博客
- 备忘录模式 (Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复。Java 设计模式・备忘录模式篇:从思想到代码实现-CSDN博客
- 解释器模式 (Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。Java 设计模式・解释器模式篇:从思想到代码实现-CSDN博客
二、迭代器模式
2.1 介绍
迭代器模式是一种行为型设计模式,其官方定义为:
提供一种方法顺序访问一个聚合对象(如集合、数组、链表等)中的各个元素,而又不暴露该对象的内部表示。
简单来说,迭代器模式的核心是将 “遍历集合元素” 的逻辑从集合类中抽离出来,封装到独立的迭代器对象中—— 无需知道集合的底层结构(数组、链表、哈希表),只需通过迭代器的统一接口(如 next()、hasNext())就能按顺序访问元素,实现 “遍历逻辑” 与 “集合本身” 的解耦。
2.2 角色
* 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
* 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
* 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
* 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
三、代码实现
为了方便理解,本文采用中文定义类名
3.1 抽象迭代器角色
public interface 数字迭代器 {
boolean hasNext();
Integer next();
}
3.2 具体迭代器角色
public class 具体数字迭代器 implements 数字迭代器 {
private List<Integer> list;
private int index;
public 具体数字迭代器(List<Integer> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public Integer next() {
return list.get(index++);
}
}
3.3 抽象聚合角色
public interface 数字容器 {
void addNumber(Integer number);
void removeNumber(Integer number);
数字迭代器 getIterator();
}
3.4 具体聚合角色
public class 具体数字容器 implements 数字容器{
private List<Integer> numbers = new ArrayList<>();
@Override
public void addNumber(Integer number) {
this.numbers.add(number);
}
@Override
public void removeNumber(Integer number) {
this.numbers.remove(number);
}
@Override
public 数字迭代器 getIterator() {
return new 具体数字迭代器(this.numbers);
}
}
3.5 客户端
public class 客户端 {
public static void main(String[] args) {
数字容器 numbers = new 具体数字容器();
numbers.addNumber(1);
numbers.addNumber(2);
numbers.addNumber(3);
numbers.addNumber(4);
numbers.removeNumber(3);
数字迭代器 iterator = numbers.getIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
1
2
4
Process finished with exit code 0
四、优缺点
4.1 优点
- 解耦遍历逻辑与聚合对象 聚合对象只负责存储数据,遍历逻辑(顺序、倒序、过滤)全部交给迭代器。即使聚合对象的内部存储结构从数组改成链表,迭代器的调用方(业务代码)也无需修改。
- 支持多种遍历方式可以为同一个聚合对象定义多个迭代器(比如正序迭代器、倒序迭代器、过滤不及格学生的迭代器),调用方按需选择,无需修改聚合对象本身。
- 符合 “单一职责原则”聚合对象专注于数据存储,迭代器专注于遍历,各自的职责更清晰,代码维护更简单。
4.2 缺点
- 增加系统复杂度引入迭代器模式需要新增迭代器接口、具体迭代器类,对于简单的聚合对象(比如只是一个普通数组),会 “过度设计”,反而增加代码量。
- 遍历效率可能降低自定义迭代器的遍历逻辑如果设计不当(比如频繁遍历聚合对象的内部数据),可能比直接遍历数组 / 集合的效率低;尤其是对于短生命周期的聚合对象,迭代器的创建和销毁会带来额外开销。
- 对简单遍历不友好如果只是需要简单的 “从头到尾遍历”,直接使用
for循环比迭代器更简洁,迭代器的抽象层反而增加了理解成本。
五、适用场景
5.1 适用场景
-
需要统一遍历不同聚合对象的场景比如你的系统中有
ArrayList、LinkedList、自定义的TreeCollection等多种数据容器,希望用统一的方式遍历(不用关心底层是数组、链表还是树),迭代器模式可以封装不同容器的遍历差异。(典型案例:Java 集合框架中的Iterator接口,ArrayList、HashMap(keySet/entrySet)都实现了该接口,遍历方式完全统一。) -
需要隐藏聚合对象内部结构的场景聚合对象不想暴露自己的内部存储(比如自定义的敏感数据容器),只允许外部通过迭代器安全遍历,避免外部直接操作内部数组 / 链表导致数据混乱。
-
需要支持多种遍历方式的场景比如对一个
OrderList,既需要正序遍历、倒序遍历,又需要按订单金额过滤遍历,此时可以为OrderList创建多个迭代器,业务代码按需调用。 -
跨语言 / 框架的通用遍历需求比如在设计通用的组件库、数据展示框架时,迭代器模式能让不同数据结构的遍历逻辑标准化,降低调用方的学习成本。
5.2 不适用场景
- 聚合对象结构极简单(比如固定长度的数组),且只需要单一遍历方式;
- 对遍历性能要求极高(比如高频次、大数据量的遍历),迭代器的抽象层会带来额外开销;
- 临时的、一次性的遍历需求(比如只在一个方法里遍历一次数组)。
六、对比学习
6.1 与增强for循环(for -each)对比学习
两者都用于遍历集合
| 维度 | 迭代器模式(Iterator) | 增强 for 循环(for-each) |
|---|---|---|
| 本质 | 设计模式,定义了遍历的抽象接口 | Java 语法糖,底层依赖 Iterator(集合)或数组索引 |
| 灵活性 | 支持删除元素(remove())、多类遍历(正序 / 倒序) |
只读遍历,无法在遍历中删除元素(会抛ConcurrentModificationException) |
| 适用范围 | 所有实现Iterable接口的聚合对象 |
数组、实现Iterable的集合(ArrayList/HashMap 等) |
| 底层依赖 | 自定义实现遍历逻辑 | 集合遍历依赖 Iterator,数组遍历依赖普通 for 循环 |
6.2 与普通for循环(直接遍历)对比学习
这是「抽象遍历」和「底层遍历」的对比,核心是「封装性」和「耦合度」的差异。
| 维度 | 迭代器模式 | 普通 for 循环(数组 / 链表索引遍历) |
|---|---|---|
| 耦合度 | 与聚合对象内部结构解耦(不用知道是数组 / 链表) | 与内部结构强耦合(数组用索引i,链表用node.next) |
| 通用性 | 统一遍历接口,换聚合对象无需改遍历代码 | 聚合对象结构变了(数组→链表),遍历代码必须改 |
| 代码复杂度 | 抽象层多,代码量稍大 | 简单直接,代码量少 |
6.3 与枚举对比学习
这是 Java 历史演进中的对比,Enumeration 是迭代器模式的 “前身”,迭代器是对它的优化。
| 维度 | 迭代器模式(Iterator) | 枚举(Enumeration) |
|---|---|---|
| 出现版本 | JDK 1.2+ | JDK 1.0+(早期遍历接口) |
| 方法名 | hasNext()、next()、remove() |
hasMoreElements()、nextElement()(无删除方法) |
| 设计思想 | 支持失败快速检测(并发修改抛异常) | 无失败快速检测,安全性低 |
| 适用场景 | 现代 Java 开发(推荐) | 仅兼容老旧代码(如 Vector、Hashtable) |
七、源码举例 java.util.Iterator
7.1 角色对应
| 迭代器模式角色 | JDK 中的具体体现 | 核心方法 |
|---|---|---|
| 迭代器接口(Iterator) | java.util.Iterator |
hasNext()、next()、remove() |
| 聚合接口(Aggregate) | java.util.Collection(通过 Iterable 接口) |
iterator()(来自 Iterable) |
| 具体聚合类 | ArrayList、LinkedList、HashMap 等 |
实现 iterator() 方法,返回具体迭代器 |
| 具体迭代器类 | 集合内部的 Itr 内部类(如 ArrayList.Itr) |
实现 Iterator 接口的所有方法 |
7.2 迭代器接口
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
7.3 聚合接口
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
7.4 具体聚合角色
public interface Collection<E> extends Iterable<E> {
...
}
7.5 具体迭代器角色(ArrayList举例)
public class ArrayList<E> extends AbstractList<E> implements List<E> {
private transient Object[] elementData; // 底层数组
private int size; // 元素个数
// 1. 实现 Iterable 接口的 iterator() 方法,返回具体迭代器
public Iterator<E> iterator() {
return new Itr();
}
// 2. 具体迭代器类(内部类):实现 Iterator 接口
private class Itr implements Iterator<E> {
int cursor; // 下一个要访问的元素索引(游标)
int lastRet = -1; // 上一个访问的元素索引(初始-1)
int expectedModCount = modCount; // 快速失败检测
// 判断是否有下一个元素:游标 < 集合大小
public boolean hasNext() {
return cursor != size;
}
// 获取下一个元素
@SuppressWarnings("unchecked")
public E next() {
// 快速失败:检测集合是否被修改(并发修改)
checkForComodification();
int i = cursor;
if (i >= size) // 超出范围抛异常
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; // 游标后移
return (E) elementData[lastRet = i]; // 返回当前元素,记录lastRet
}
// 删除上一个访问的元素
public void remove() {
if (lastRet < 0) // 未访问过元素,抛异常
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); // 调用ArrayList的remove方法
cursor = lastRet; // 游标回退(因为删除后元素前移)
lastRet = -1; // 重置lastRet
expectedModCount = modCount; // 更新modCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 快速失败检测:modCount 是集合修改次数,expectedModCount 是迭代器创建时的次数
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
八、其他相关设计模式
更多推荐




所有评论(0)