这是 2020 年 8 月整理的设计模式专题收官篇。迭代器模式的笔记只有一句话定义,本文补全了完整示例。它是 Java 集合框架的地基——你每天都在用 for-each 遍历集合,底层就是迭代器在跑。
基本说明
迭代器模式(Iterator Pattern):提供一种方法,顺序访问一个集合的各个对象,而不暴露该对象的内部实现。
- 应用:比如 JDK 的集合类的 Iterator(
ArrayList、HashSet等都有迭代器)。
客户端 ──> 迭代器(Iterator)──> 集合(Aggregate)
hasNext() / next() (内部结构:数组 / 链表 / 哈希表)
(客户端只跟迭代器打交道,不知道集合内部长什么样)
为什么需要迭代器
痛点:遍历不同结构的集合,方式完全不同:
// 数组:用下标
for (int i = 0; i < array.length; i++) { ... }
// 链表:用指针
while (node != null) { node = node.next; ... }
// 哈希表:要遍历桶
for (Entry e : map.entrySet()) { ... }
问题:客户端要遍历集合,就必须知道集合的底层结构——数组用下标、链表用指针、Hash 用桶。一旦集合结构变了(ArrayList 换 LinkedList),所有遍历代码都要改。
迭代器解法:把”怎么遍历”封装进迭代器,客户端只依赖统一的 hasNext()/next() 接口,集合内部结构对客户端完全透明。
代码实现
手写一个简单的迭代器,遍历自定义的”课程集合”:
/**
* 描述:自定义集合:课程列表
*/
public class CourseList {
private Course[] courses = new Course[10];
private int size = 0;
public void add(Course course) {
courses[size++] = course;
}
// 返回迭代器,客户端不直接接触数组
public CourseIterator iterator() {
return new CourseIteratorImpl(this);
}
Course[] getCourses() {
return courses;
}
int getSize() {
return size;
}
}
/**
* 描述:迭代器接口(对应 JDK 的 Iterator)
*/
public interface CourseIterator {
boolean hasNext(); // 是否还有下一个
Course next(); // 返回下一个元素
}
/**
* 描述:迭代器实现:内部持有数组下标游标
*/
public class CourseIteratorImpl implements CourseIterator {
private CourseList courseList;
private int position = 0; // 游标:当前遍历位置
public CourseIteratorImpl(CourseList courseList) {
this.courseList = courseList;
}
@Override
public boolean hasNext() {
return position < courseList.getSize();
}
@Override
public Course next() {
return courseList.getCourses()[position++];
}
}
/**
* 描述:客户端
*/
public class Client {
public static void main(String[] args) {
CourseList list = new CourseList();
list.add(new Course("Java"));
list.add(new Course("Python"));
// 客户端只依赖迭代器接口,不知道底层是数组
CourseIterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
三个角色:
- Aggregate(集合,CourseList):提供创建迭代器的
iterator()方法; - Iterator(迭代器接口,CourseIterator):
hasNext()+next(); - ConcreteIterator(具体迭代器,CourseIteratorImpl):持有游标,实现遍历逻辑。
核心:即使 CourseList 底层把数组换成链表,客户端的遍历代码一行都不用改——hasNext()/next() 接口不变。
JDK 集合框架中的迭代器
Java 集合框架把迭代器模式用得炉火纯青:
| 集合 | 底层结构 | 迭代方式 |
|---|---|---|
ArrayList | 数组 | 下标递增 |
LinkedList | 双向链表 | 指针后移 |
HashSet | 哈希表 | 遍历桶内链表 |
TreeSet | 红黑树 | 中序遍历 |
共同点:都实现了 Iterator 接口的 hasNext()/next()/remove()。所以客户端可以用统一的方式遍历所有集合——这正是 for-each 的本质:
// for-each 语法糖,编译后等价于迭代器遍历
for (Course c : courseList) {
System.out.println(c.getName());
}
迭代器特性:
- 快速失败(fail-fast):遍历过程中集合被修改(增删元素),抛出
ConcurrentModificationException(modCount 机制); - 单向遍历:一般只能从前到后(
ListIterator支持双向); - 只读遍历:迭代器主要用于读取,修改集合推荐用集合自身的 API。
适用场景与优缺点
适用场景:
- 需要不暴露内部结构地遍历集合元素;
- 需要为不同集合提供统一的遍历方式;
- 希望遍历逻辑与集合本身解耦(集合只管存,迭代器管遍历)。
优点:
- 客户端不依赖集合内部实现,封装性好;
- 遍历方式统一,简化客户端代码;
- 集合结构变化不影响遍历代码。
缺点:
- 类数量增加(每个集合都要配一个迭代器);
- 迭代器一般是单向、只读的,功能受限(需要双向遍历要用 ListIterator)。
小结
- 迭代器模式提供统一方法顺序访问集合,不暴露内部实现。
- 三个角色:集合(Aggregate)、迭代器接口(hasNext/next)、具体迭代器(持游标)。
- 价值:集合结构透明化——数组换链表,客户端遍历代码不用改。
- 应用:JDK 集合框架、for-each 语法糖、Netty 等框架的集合遍历。
- 一句话:只给你 hasNext 和 next,集合里面长什么样你不需要知道。