1. 迭代器模式概述
1.1 什麼是迭代器模式?
迭代器模式是一種行為型設計模式,其核心思想是提供一種方法來訪問聚合對象中的元素,而無需暴露聚合的內部結構。根據GoF(Gang of Four)在《設計模式:可複用面向對象軟件的基礎》中的定義,迭代器模式的目的是:“提供一種方法順序訪問一個聚合對象中各個元素,而又不需暴露該對象的內部表示。”
在迭代器模式中,迭代器(Iterator)負責遍歷邏輯,聚合對象(Aggregate)提供創建迭代器的接口。客户端通過迭代器獲取元素序列,而不關心聚合是數組、鏈表、樹還是自定義結構。這實現了遍歷與聚合的解耦,支持多種遍歷方式(如順序、逆序、過濾)。
迭代器模式符合開閉原則(OCP),允許在不修改聚合類的情況下添加新迭代器;同時支持單一職責原則(SRP),聚合專注數據管理,迭代器專注遍歷。Java中,java.util.Iterator和java.lang.Iterable是標準實現,廣泛用於Collection框架。
與歷史對話中的解釋器模式(語法樹遞歸解釋)不同,迭代器模式更注重順序訪問而非樹狀解釋;與命令模式(請求封裝,支持Undo)相比,迭代器處理數據流而非行為歷史;與責任鏈模式(線性傳遞)類似,但迭代器是拉取式(pull)而非推送式;與策略模式(算法切換)不同,它參數化遍歷行為而非業務邏輯。
迭代器模式的本質是遍歷抽象化與內部隱藏。它將訪問邏輯從數據結構中分離,便於管理複雜集合,尤其在需要統一遍歷接口的系統中表現出色。
1.2 迭代器模式的核心組成
迭代器模式通常涉及以下幾個關鍵角色:
- Iterator(迭代器接口):聲明遍歷操作,如
hasNext()、next(),有時包括remove()。 - ConcreteIterator(具體迭代器):實現Iterator接口,維護當前遊標、聚合引用。
- Aggregate(聚合接口):聲明創建迭代器的工廠方法,如
iterator()。 - ConcreteAggregate(具體聚合):實現Aggregate,提供具體迭代器實例。
- Client(客户端):使用聚合的迭代器遍歷元素。
迭代器模式的工作流程如下:
- 客户端獲取聚合的迭代器。
- 客户端循環調用
hasNext()檢查、next()獲取元素。 - 迭代器內部管理位置,避免客户端直接訪問聚合。
- 可選支持
remove()安全刪除。
迭代器模式的靈活性在於,支持外部迭代器(客户端控制)和內部迭代器(聚合控制回調),以及失效安全(fail-fast)機制檢測併發修改。
2. 迭代器模式的原理與結構
2.1 迭代器模式的UML類圖
以下是迭代器模式的典型UML類圖,展示其結構:
+------------------+ +------------------+
| Client | | Aggregate |
+------------------+ +------------------+
| |<----->| + iterator() |
+------------------+ +------------------+
^
|
+--------------------+--------------------+
| |
+---------------------+ +---------------------+
| Iterator | | ConcreteIterator |
+---------------------+ +---------------------+
| + hasNext() |<-----------------| - aggregate |
| + next() | | - cursor |
| + remove() (opt) | | + hasNext() |
+---------------------+ | + next() |
^ +---------------------+
| ^
| |
+------------------+
| ConcreteAggregate|
+------------------+
| + iterator() |
| - elements |
+------------------+
- Client:使用迭代器遍歷。
- Aggregate:工廠接口。
- Iterator:遍歷協議。
- ConcreteIterator:狀態管理。
- ConcreteAggregate:數據容器。
此結構支持多迭代器併發遍歷。
2.2 迭代器模式的變體
迭代器模式根據遍歷需求可分為以下變體:
- 外部迭代器:客户端拉取元素(Java標準)。
- 內部迭代器:聚合推送元素到回調(如Java 8 forEach)。
- 失效快速迭代器:併發修改拋
ConcurrentModificationException。 - 失效安全迭代器:複製快照,支持修改(如
CopyOnWriteArrayList)。 - 過濾/映射迭代器:裝飾器模式擴展,支持條件遍歷。
- 樹狀迭代器:深度優先、廣度優先遍歷。
在Java中,ListIterator擴展支持雙向遍歷;Stream API是函數式迭代器。
3. 迭代器模式的Java實現
為深入理解迭代器模式,以下通過三個場景展示其Java實現:自定義數組列表迭代器(基本版)、鏈表逆序迭代器(雙向版)和樹狀結構深度遍歷迭代器(複合版)。
3.1 自定義數組列表:基本順序迭代器
迭代器模式常用於集合遍歷,模擬ArrayList。
3.1.1 聚合接口與具體聚合
// 聚合接口
interface MyCollection<E> {
MyIterator<E> iterator();
void add(E element);
int size();
}
// 具體聚合:數組列表
class MyArrayList<E> implements MyCollection<E> {
private Object[] elements = new Object[10];
private int size = 0;
@Override
public void add(E element) {
if (size == elements.length) {
elements = Arrays.copyOf(elements, size * 2);
}
elements[size++] = element;
}
@Override
public int size() {
return size;
}
@SuppressWarnings("unchecked")
public E get(int index) {
if (index >= size) throw new IndexOutOfBoundsException();
return (E) elements[index];
}
@Override
public MyIterator<E> iterator() {
return new ArrayIterator();
}
private class ArrayIterator implements MyIterator<E> {
private int cursor = 0;
private int expectedModCount = modCount; // 失效快速
@Override
public boolean hasNext() {
checkForComodification();
return cursor < size;
}
@SuppressWarnings("unchecked")
@Override
public E next() {
checkForComodification();
if (cursor >= size) throw new NoSuchElementException();
return (E) elements[cursor++];
}
@Override
public void remove() {
// 簡化實現:不支持或拋異常
throw new UnsupportedOperationException();
}
private void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
3.1.2 迭代器接口
// 迭代器接口
interface MyIterator<E> {
boolean hasNext();
E next();
void remove(); // 可選
}
3.1.3 客户端代碼
public class ArrayIteratorDemo {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("蘋果");
list.add("香蕉");
list.add("橙子");
MyIterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 輸出:蘋果 香蕉 橙子
}
}
運行結果:
蘋果
香蕉
橙子
分析:此示例實現失效快速機制(modCount)。內部類訪問私有字段。易擴展支持remove。
3.2 鏈表:支持逆序的雙向迭代器
擴展為雙向鏈表,支持正序/逆序。
3.2.1 節點與聚合
// 節點
class Node<E> {
E data;
Node<E> prev, next;
Node(E data) {
this.data = data;
}
}
// 具體聚合:雙向鏈表
class MyLinkedList<E> implements MyCollection<E> {
private Node<E> head, tail;
private int size = 0;
private int modCount = 0;
@Override
public void add(E element) {
Node<E> newNode = new Node<>(element);
if (tail == null) {
head = tail = newNode;
} else {
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
size++;
modCount++;
}
@Override
public int size() {
return size;
}
@Override
public MyIterator<E> iterator() {
return new ForwardIterator();
}
public MyIterator<E> reverseIterator() {
return new ReverseIterator();
}
// 正序迭代器
private class ForwardIterator implements MyIterator<E> {
private Node<E> current = new Node<>(null); // 哨兵
{ current.next = head; }
private int expectedModCount = modCount;
@Override
public boolean hasNext() {
check();
return current.next != null;
}
@Override
public E next() {
check();
current = current.next;
return current.data;
}
@Override
public void remove() {
// 實現鏈表刪除,更新modCount
throw new UnsupportedOperationException(); // 簡化
}
private void check() {
if (expectedModCount != modCount) throw new ConcurrentModificationException();
}
}
// 逆序迭代器
private class ReverseIterator implements MyIterator<E> {
private Node<E> current = new Node<>(null);
{ current.prev = tail; }
private int expectedModCount = modCount;
@Override
public boolean hasNext() {
check();
return current.prev != null;
}
@Override
public E next() {
check();
current = current.prev;
return current.data;
}
private void check() {
if (expectedModCount != modCount) throw new ConcurrentModificationException();
}
}
}
3.2.2 客户端代碼
public class LinkedIteratorDemo {
public static void main(String[] args) {
MyLinkedList<String> list = new MyLinkedList<>();
list.add("A");
list.add("B");
list.add("C");
System.out.println("正序:");
MyIterator<String> forward = list.iterator();
while (forward.hasNext()) {
System.out.println(forward.next());
}
System.out.println("逆序:");
MyIterator<String> reverse = list.reverseIterator();
while (reverse.hasNext()) {
System.out.println(reverse.next());
}
}
}
運行結果:
正序:
A
B
C
逆序:
C
B
A
分析:此示例支持多迭代器變體。哨兵節點簡化邊界。適用於需要靈活遍歷的結構。
3.3 樹狀結構:深度優先迭代器
結合複合模式,遍歷二叉樹。
3.3.1 樹節點與聚合
// 樹節點
class TreeNode<E> {
E data;
TreeNode<E> left, right;
TreeNode(E data) {
this.data = data;
}
}
// 具體聚合:二叉樹
class BinaryTree<E> implements MyCollection<E> {
private TreeNode<E> root;
private int size = 0;
public void insert(E data) {
// 簡化:構建樹
if (root == null) {
root = new TreeNode<>(data);
} else {
// 實際插入邏輯省略
}
size++;
}
@Override
public int size() {
return size;
}
@Override
public MyIterator<E> iterator() {
return new DFSIterator();
}
// 深度優先迭代器(非遞歸,使用棧)
private class DFSIterator implements MyIterator<E> {
private Stack<TreeNode<E>> stack = new Stack<>();
public DFSIterator() {
if (root != null) pushLeft(root);
}
private void pushLeft(TreeNode<E> node) {
while (node != null) {
stack.push(node);
node = node.left;
}
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public E next() {
if (!hasNext()) throw new NoSuchElementException();
TreeNode<E> node = stack.pop();
pushLeft(node.right);
return node.data;
}
}
}
3.3.2 客户端代碼(構建樹)
public class TreeIteratorDemo {
public static void main(String[] args) {
BinaryTree<Integer> tree = new BinaryTree<>();
// 構建樹:根1,左2右3,2左4
// 手動構建
tree.root = new TreeNode<>(1);
tree.root.left = new TreeNode<>(2);
tree.root.right = new TreeNode<>(3);
tree.root.left.left = new TreeNode<>(4);
tree.size = 4;
MyIterator<Integer> it = tree.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 輸出:4 2 1 3 (中序變體)
}
}
分析:非遞歸DFS使用棧,避免遞歸溢出。易擴展BFS(隊列)。
4. 迭代器模式的應用場景
4.1 集合框架遍歷
- Java Collections:
ArrayList.iterator()。 - 數據庫結果集:
ResultSet。
4.2 數據流處理
- 文件行迭代:
BufferedReader.lines()。 - 網絡流:字節迭代器。
4.3 樹/圖遍歷
- DOM樹:節點迭代。
- 文件系統目錄遍歷。
4.4 自定義數據結構
- 優先隊列、跳錶迭代。
- 分頁數據懶加載。
4.5 其他場景
- 遊戲實體遍歷:場景對象迭代。
- 日誌記錄器:事件流迭代。
5. 迭代器模式的優缺點
5.1 迭代器模式的優點
- 解耦性:遍歷與聚合分離。
- 統一接口:多數據結構統一遍歷。
- 支持多遍歷:併發、正逆序。
- 安全性:隱藏內部,失效檢測。
- 擴展性:易添加過濾/轉換迭代器。
5.2 迭代器模式的缺點
- 開銷增加:簡單數組直接索引更快。
- 狀態管理:多迭代器需跟蹤遊標。
- 不支持隨機訪問:順序迭代侷限。
- 併發複雜:失效機制需同步。
- 內存消耗:棧/隊列輔助結構。
6. 迭代器模式與其他設計模式的對比
6.1 迭代器模式 vs. 解釋器模式
- 迭代器模式:順序訪問元素。
- 解釋器模式(歷史對話):樹遞歸解釋。
迭代器可遍歷AST節點。
6.2 迭代器模式 vs. 命令模式
- 迭代器模式:數據拉取。
- 命令模式:行為執行。
命令隊列可用迭代器遍歷。
6.3 迭代器模式 vs. 責任鏈模式
- 迭代器模式:拉取式遍歷。
- 責任鏈模式:推送式處理。
鏈節點可用迭代器訪問。
6.4 迭代器模式 vs. 訪問者模式
- 迭代器模式:客户端控制遍歷。
- 訪問者模式:元素接受訪問者。
結合使用:迭代器提供元素,訪問者操作。
6.5 與複合模式結合
樹迭代器遍歷複合結構。
7. 迭代器模式在實際項目中的案例分析
7.1 Java Collection Framework
List<String> list = new ArrayList<>();
list.add("Java");
for (String s : list) { // 增強for使用iterator
System.out.println(s);
}
內部ArrayList.Itr實現失效快速。
7.2 Stream API
list.stream().filter(s -> s.startsWith("J")).forEach(System.out::println);
Spliterator是並行迭代器。
7.3 JDBC ResultSet
while (rs.next()) {
System.out.println(rs.getString(1));
}
行迭代器封裝數據庫遊標。
8. 迭代器模式的注意事項與最佳實踐
8.1 設計注意事項
- 失效機制:使用modCount檢測修改。
- 移除安全:remove後調整遊標。
- 空聚合:hasNext返回false。
- 線程安全:同步或CopyOnWrite。
8.2 性能優化
- 懶加載:生成器式迭代器。
- 批量next:減少方法調用。
- 並行:Spliterator支持fork。
- 緩存:預取元素。
Spliterator<String> spliterator = list.spliterator();
spliterator.forEachRemaining(System.out::println);
8.3 常見坑點
- ConcurrentModificationException:迭代中修改。
- 遊標越界:未檢查hasNext。
- 內存泄漏:長生命週期迭代器持聚合。
- 無限迭代:循環結構未終止。
8.4 結合Java現代特性
Java 8+ Iterable默認方法:
interface MyCollection<E> extends Iterable<E> {
default void forEach(Consumer<? super E> action) {
for (E e : this) action.accept(e);
}
}
Record簡化節點。