一、引言
迭代子模式 (Iterator Pattern),又称为迭代器模式,是设计模式中行为型模式的一种。它提供了一种顺序访问集合对象中各个元素的方法,而又不需要暴露该对象的内部表示。
在Java开发中,我们经常需要遍历各种集合类型,如List、Set、Map等。迭代器模式为我们提供了一种统一的遍历接口 ,使得我们可以用相同的方式遍历不同的集合结构,这大大提高了代码的复用性 和灵活性。
1.1 模式定义
迭代器模式定义如下:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
1.2 在Java中的重要性
- Java集合框架广泛使用迭代器模式
- 为不同集合类型提供统一的遍历接口
- 支持多种遍历策略(正向、反向、并发遍历等)
- 是现代编程语言中不可或缺的设计模式之一
二、核心概念
迭代器模式主要包含以下四个核心角色:
2.1 角色组成
(1)抽象迭代器(Iterator)
定义遍历聚合对象所需的接口,通常包含以下方法:
hasNext():判断是否还有下一个元素next():获取下一个元素remove():可选操作,删除当前元素
(2)具体迭代器(ConcreteIterator)
实现抽象迭代器接口,跟踪遍历过程中的当前位置,负责管理遍历状态。
(3)抽象聚合(Aggregate)
定义创建相应迭代器对象的接口,通常声明一个iterator()方法。
(4)具体聚合(ConcreteAggregate)
实现抽象聚合接口,返回一个具体迭代器的实例。
2.2 类图说明
┌─────────────────┐ ┌─────────────────┐
│ <<interface>>│ │ <<interface>>│
│ Iterator │ │ Aggregate │
├─────────────────┤ ├─────────────────┤
│ +hasNext():bool │ │ +iterator():Iterator│
│ +next():Object │ └─────────────────┘
│ +remove():void │ △
└─────────────────┘ │
△ │
│ ┌─────────────────┐
│ │ConcreteAggregate│
┌─────────────────┐ ├─────────────────┤
│ConcreteIterator│ │ +iterator():... │
├─────────────────┤ └─────────────────┘
│ +hasNext():bool │ │
│ +next():Object │ │
│ +remove():void │ │
├─────────────────┤ │
│ -position:int │ │
└─────────────────┘ │
│ │
└─────────────────────────┘
三、实现步骤
下面通过一个完整的Java代码示例来展示迭代器模式的实现过程。我们将实现一个自定义的**书架(BookShelf)**及其迭代器。
3.1 实现步骤概述
- 定义Iterator接口
- 定义Aggregate接口
- 实现具体迭代器BookShelfIterator
- 实现具体聚合BookShelf
- 定义元素对象Book
- 客户端测试代码
3.2 完整代码实现
(1)定义Iterator接口
java
/**
* 迭代器接口
*/
public interface Iterator {
/**
* 判断是否有下一个元素
* @return true表示还有下一个元素,false表示已经到达末尾
*/
boolean hasNext();
/**
* 获取下一个元素
* @return 下一个元素对象
*/
Object next();
}
(2)定义Aggregate接口
java
/**
* 聚合接口,定义创建迭代器的方法
*/
public interface Aggregate {
/**
* 创建对应的迭代器
* @return 迭代器对象
*/
Iterator iterator();
}
(3)定义Book类
java
/**
* 书籍类,作为集合中的元素
*/
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "《" + name + "》";
}
}
(4)实现具体迭代器BookShelfIterator
java
/**
* 书架迭代器,具体迭代器实现
*/
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
(5)实现具体聚合BookShelf
java
/**
* 书架类,具体聚合实现
*/
public class BookShelf implements Aggregate {
private Book[] books;
private int last = 0;
public BookShelf(int maxSize) {
this.books = new Book[maxSize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
(6)客户端测试代码
java
/**
* 客户端测试类
*/
public class Main {
public static void main(String[] args) {
// 创建书架
BookShelf bookShelf = new BookShelf(4);
// 添加书籍
bookShelf.appendBook(new Book("Java编程思想"));
bookShelf.appendBook(new Book("设计模式"));
bookShelf.appendBook(new Book("算法导论"));
bookShelf.appendBook(new Book("重构:改善既有代码的设计"));
// 使用迭代器遍历书架
System.out.println("=== 书架书籍列表 ===");
Iterator iterator = bookShelf.iterator();
while (iterator.hasNext()) {
Book book = (Book) iterator.next();
System.out.println(book);
}
}
}
(7)运行结果
=== 书架书籍列表 ===
《Java编程思想》
《设计模式》
《算法导论》
《重构:改善既有代码的设计》
3.3 泛型版本实现(推荐)
为了提高类型安全性,我们可以使用泛型来改进上述实现:
java
/**
* 泛型迭代器接口
*/
public interface Iterator<T> {
boolean hasNext();
T next();
}
/**
* 泛型书架迭代器
*/
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Book next() {
return bookShelf.getBookAt(index++);
}
}
/**
* 泛型客户端代码
*/
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Java编程思想"));
bookShelf.appendBook(new Book("设计模式"));
// 使用泛型迭代器,无需类型转换
Iterator<Book> iterator = bookShelf.iterator();
while (iterator.hasNext()) {
Book book = iterator.next(); // 直接获得Book类型
System.out.println(book);
}
}
}
四、应用场景
4.1 典型应用场景
(1)集合遍历
最常见的就是遍历各种集合类型,如List、Set等。
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
// 使用迭代器遍历
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String lang = it.next();
System.out.println(lang);
}
(2)数据库查询结果集
JDBC中的ResultSet本质上也是一个迭代器。
java
// ResultSet 迭代模式示例
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) { // 类似 hasNext() + next()
String name = rs.getString("name");
System.out.println(name);
}
(3)文件系统遍历
遍历文件夹中的文件。
java
// 伪代码示例
FileIterator fileIterator = new FileIterator("/path/to/directory");
while (fileIterator.hasNext()) {
File file = fileIterator.next();
processFile(file);
}
(4)菜单导航
树形结构的菜单遍历。
java
// 菜单迭代器示例
MenuItemIterator menuIterator = restaurantMenu.iterator();
while (menuIterator.hasNext()) {
MenuItem item = menuIterator.next();
displayMenuItem(item);
}
(5)反向迭代
某些场景需要反向遍历集合。
java
// 反向迭代器示例
Iterator<String> reverseIterator = new ReverseIterator<>(list);
while (reverseIterator.hasNext()) {
System.out.println(reverseIterator.next());
}
4.2 应用优势
- 简化集合遍历:提供统一的遍历方式,简化客户端代码
- 封装内部结构:客户端无需知道集合的内部实现细节
- 支持多种遍历策略:可以轻松实现不同的遍历方式(正向、反向、跳跃等)
- 符合单一职责原则:将遍历逻辑从集合类中分离出来
五、优缺点分析
5.1 优点
(1)符合单一职责原则
迭代器模式将遍历操作 与聚合对象的职责分离,使得聚合对象专注于数据管理,迭代器专注于遍历逻辑。
(2)符合开闭原则
当需要新增遍历方式时,只需新增迭代器类,无需修改聚合类的代码。
(3)简化客户端代码
客户端只需要与Iterator接口交互,无需了解聚合对象的内部结构。
(4)支持多种遍历方式**
可以同时存在多个迭代器对象,支持正向、反向、跳跃等多种遍历策略。
java
// 同时使用多个迭代器
List<String> list = Arrays.asList("A", "B", "C", "D");
Iterator<String> forward = list.iterator();
Iterator<String> reverse = new ReverseIterator<>(list);
// 两个迭代器互不干扰
(5)封装性良好
客户端只能通过迭代器访问集合元素,无法直接操作集合内部结构,提高了安全性。
5.2 缺点
(1)增加类的数量
每个聚合类都需要对应一个迭代器类,增加了系统的复杂性。
传统方式:1个集合类
迭代器模式:1个集合类 + 1个迭代器类
(2)对于简单集合可能过度设计
对于简单的数组遍历,使用迭代器模式可能是"杀鸡用牛刀"。
(3)存储遍历状态需要额外空间
迭代器需要记录当前遍历位置,对于某些数据结构可能需要额外的存储空间。
java
// 双向链表的迭代器可能需要存储更多状态
public class LinkedListIterator {
private Node currentNode; // 当前节点
private Node lastReturned; // 最后返回的节点
private int expectedModCount; // 修改计数器
}
(4)并发遍历可能存在问题**
如果多个迭代器同时遍历同一个集合,且集合在遍历过程中被修改,可能产生不可预期的结果。
java
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
Iterator<String> it1 = list.iterator();
Iterator<String> it2 = list.iterator();
it1.next();
list.add("C"); // 修改集合
it2.next(); // 可能抛出 ConcurrentModificationException
六、JDK中的应用
Java集合框架是迭代器模式的典型应用,几乎所有的集合类都实现了迭代器模式。
6.1 Iterator接口
JDK中定义的核心迭代器接口:
java
package java.util;
/**
* 集合的迭代器接口
*/
public interface Iterator<E> {
/**
* 判断是否还有下一个元素
*/
boolean hasNext();
/**
* 返回下一个元素
*/
E next();
/**
* 从底层集合中移除此迭代器返回的最后一个元素(可选操作)
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* 对每个剩余元素执行给定操作(Java 8新增)
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
6.2 Iterable接口
所有可迭代集合都需要实现此接口:
java
package java.util;
/**
* 实现此接口允许对象成为 "foreach" 语句的目标
*/
public interface Iterable<T> {
/**
* 返回一个类型为 T 的元素的迭代器
*/
Iterator<T> iterator();
/**
* Java 8新增:默认的forEach方法
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
/**
* Java 8新增:返回并行迭代器
*/
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
6.3 典型实现示例
(1)ArrayList的迭代器实现
java
// ArrayList内部类
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 快速失败机制检查
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
(2)ListIterator接口(增强版迭代器)
java
package java.util;
/**
* 列表迭代器,允许程序员在任一方向遍历列表
*/
public interface ListIterator<E> extends Iterator<E> {
// 继承自Iterator的方法
boolean hasNext();
E next();
// 新增的反向遍历方法
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
// 修改操作
void set(E e);
void add(E e);
}
6.4 快速失败机制(Fail-Fast)
Java集合框架中的迭代器实现了快速失败机制,用于检测并发修改:
java
public class FailFastExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String str = it.next();
if (str.equals("B")) {
// 在遍历过程中修改集合
list.remove(str); // 抛出 ConcurrentModificationException
}
}
}
}
快速失败机制的原理:
- 集合内部维护一个
modCount(修改计数器) - 迭代器创建时记录当前的
modCount - 每次迭代时检查集合的
modCount是否改变 - 如果发现变化,立即抛出
ConcurrentModificationException
6.5 JDK中的迭代器使用示例
java
public class JDKIteratorExample {
public static void main(String[] args) {
// List迭代器示例
System.out.println("=== List迭代器 ===");
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
// 方式1:使用Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 方式2:使用for-each(语法糖,底层也是迭代器)
System.out.println("\n=== for-each循环 ===");
for (String lang : list) {
System.out.println(lang);
}
// 方式3:使用ListIterator(支持双向遍历)
System.out.println("\n=== ListIterator反向遍历 ===");
ListIterator<String> lit = list.listIterator(list.size());
while (lit.hasPrevious()) {
System.out.println(lit.previous());
}
// Set迭代器示例
System.out.println("\n=== Set迭代器 ===");
Set<Integer> set = new HashSet<>();
set.add(3);
set.add(1);
set.add(2);
Iterator<Integer> setIt = set.iterator();
while (setIt.hasNext()) {
System.out.println(setIt.next());
}
// Map迭代器示例
System.out.println("\n=== Map迭代器 ===");
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Orange", 15);
// 遍历Key
System.out.println("--- 遍历Key ---");
Iterator<String> keyIterator = map.keySet().iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
System.out.println(key + ": " + map.get(key));
}
// 遍历Entry
System.out.println("--- 遍历Entry ---");
Iterator<Map.Entry<String, Integer>> entryIterator =
map.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
七、总结
7.1 核心要点回顾
迭代器模式是一种简单而强大的设计模式,它通过以下方式解决了集合遍历问题:
| 方面 | 说明 |
|---|---|
| 核心思想 | 将遍历操作从集合对象中分离出来 |
| 主要角色 | Iterator(迭代器)、Aggregate(聚合)、具体实现类 |
| 关键优势 | 统一接口、封装细节、支持多种遍历策略 |
| JDK应用 | Collection框架、Iterable接口、Iterator接口 |
7.2 使用建议
(1)何时使用迭代器模式?
✅ 推荐使用的场景:
- 需要为聚合对象提供多种遍历方式
- 需要遍历复杂的聚合结构(如树、图)
- 希望隐藏聚合对象的内部实现细节
- 需要支持同一个聚合对象上的多个遍历
❌ 不推荐使用的场景:
- 简单的数组遍历
- 只有单一遍历方式且不会变化的场景
- 性能要求极高的场景(避免额外开销)
(2)最佳实践
-
优先使用泛型:提高类型安全性
javaIterator<Book> it = bookShelf.iterator(); // 推荐 Iterator it = bookShelf.iterator(); // 不推荐 -
考虑使用for-each语法:简化代码
java// 推荐:简洁明了 for (Book book : bookShelf) { System.out.println(book); } -
注意并发修改问题:使用迭代器的remove方法
javaIterator<String> it = list.iterator(); while (it.hasNext()) { String str = it.next(); if (str.equals("B")) { it.remove(); // 正确的删除方式 } } -
实现合适的迭代器类型:根据需要选择Iterator或ListIterator
javaListIterator<Book> lit = list.listIterator(); // 支持双向遍历
(3)扩展思考
与Java 8 Stream API的关系:
Java 8引入的Stream API可以看作是迭代器模式的增强版:
java
// 传统迭代器方式
Iterator<Book> it = bookList.iterator();
while (it.hasNext()) {
Book book = it.next();
if (book.getPrice() > 50) {
System.out.println(book.getName());
}
}
// Stream API方式(函数式编程风格)
bookList.stream()
.filter(book -> book.getPrice() > 50)
.map(Book::getName)
.forEach(System.out::println);
Stream API提供了:
- 更强的声明式编程能力
- 支持并行处理
- 丰富的中间操作 和终端操作
- 更好的性能优化潜力
但这并不意味着迭代器模式过时了,它们各有适用场景:
- 迭代器:适合需要精细控制遍历过程的场景
- Stream:适合声明式的数据处理流水线
7.3 结语
迭代器模式是设计模式中应用最广泛的模式之一,它体现了封装变化 和单一职责的设计原则。通过学习和掌握迭代器模式,我们不仅能够更好地理解Java集合框架的设计思想,还能在自己的项目中灵活运用这一模式,编写出更加优雅、可维护的代码。
在Java开发中,合理使用迭代器模式可以:
- 提高代码的可读性 和可维护性
- 增强模块化程度
- 实现高内聚、低耦合的设计目标
- 为后续的扩展 和优化提供良好基础