迭代器模式:統一不同數據結構的遍歷方式
一、模式核心:分離數據遍歷與數據表示
在開發中,我們經常需要遍歷不同的數據結構,如數組、鏈表、樹等。若在客戶端代碼中直接編寫遍歷邏輯,不僅會導致代碼冗余,而且當數據結構發生變化時,遍歷邏輯也需要隨之修改。迭代器模式(Iterator Pattern 通過將遍歷邏輯封裝成獨立的迭代器對象,實現數據結構與遍歷算法的解耦,核心解決:
- 統一遍歷接口:為不同數據結構提供一致的遍歷方式,如
hasNext()
、next()
。 - 隱藏數據結構細節:客戶端無需了解數據存儲的具體結構(如鏈表的節點操作),僅通過迭代器操作數據。
- 支持復雜遍歷:方便實現倒序遍歷、跳躍遍歷等特殊遍歷需求。
核心思想與 UML 類圖
迭代器模式主要包含迭代器接口、具體迭代器、聚合接口和具體聚合四個角色:
二、核心實現:自定義數組與鏈表的統一遍歷
1. 定義迭代器接口(規范遍歷操作)
public interface Iterator {boolean hasNext();Object next();
}
2. 定義聚合接口(提供創建迭代器的方法)
public interface Aggregate {Iterator createIterator();
}
3. 實現具體聚合類(以數組為例)
public class ArrayAggregate implements Aggregate {private Object[] items;private int size;public ArrayAggregate(int capacity) {items = new Object[capacity];}public void addItem(Object item) {items[size++] = item;}public int size() {return size;}public Object getItem(int index) {return items[index];}@Overridepublic Iterator createIterator() {return new ArrayIterator(this);}// 具體迭代器類(內部類)private class ArrayIterator implements Iterator {private ArrayAggregate aggregate;private int index = 0;public ArrayIterator(ArrayAggregate aggregate) {this.aggregate = aggregate;}@Overridepublic boolean hasNext() {return index < aggregate.size();}@Overridepublic Object next() {return aggregate.getItem(index++);}}
}
4. 實現鏈表聚合類及對應迭代器
// 鏈表節點類
class ListNode {Object data;ListNode next;public ListNode(Object data) {this.data = data;}
}// 鏈表聚合類
public class ListAggregate implements Aggregate {private ListNode head;public void addItem(Object item) {ListNode newNode = new ListNode(item);if (head == null) {head = newNode;} else {ListNode current = head;while (current.next != null) {current = current.next;}current.next = newNode;}}@Overridepublic Iterator createIterator() {return new ListIterator(head);}// 鏈表迭代器類private class ListIterator implements Iterator {private ListNode current;public ListIterator(ListNode head) {this.current = head;}@Overridepublic boolean hasNext() {return current != null;}@Overridepublic Object next() {Object data = current.data;current = current.next;return data;}}
}
5. 客戶端調用示例
public class ClientDemo {public static void main(String[] args) {// 使用數組聚合類ArrayAggregate arrayAggregate = new ArrayAggregate(3);arrayAggregate.addItem("Apple");arrayAggregate.addItem("Banana");arrayAggregate.addItem("Cherry");Iterator arrayIterator = arrayAggregate.createIterator();while (arrayIterator.hasNext()) {System.out.println(arrayIterator.next());}// 使用鏈表聚合類ListAggregate listAggregate = new ListAggregate();listAggregate.addItem("Dog");listAggregate.addItem("Elephant");listAggregate.addItem("Fox");Iterator listIterator = listAggregate.createIterator();while (listIterator.hasNext()) {System.out.println(listIterator.next());}}
}
三、進階:實現倒序遍歷與并發安全迭代器
1. 倒序遍歷迭代器
public class ReverseArrayIterator implements Iterator {private ArrayAggregate aggregate;private int index;public ReverseArrayIterator(ArrayAggregate aggregate) {this.aggregate = aggregate;this.index = aggregate.size() - 1;}@Overridepublic boolean hasNext() {return index >= 0;}@Overridepublic Object next() {return aggregate.getItem(index--);}
}// 客戶端調用倒序遍歷
ArrayAggregate arrayAggregate = new ArrayAggregate(3);
// 添加元素...
Iterator reverseIterator = new ReverseArrayIterator(arrayAggregate);
while (reverseIterator.hasNext()) {System.out.println(reverseIterator.next());
}
2. 并發安全迭代器(使用讀寫鎖)
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ThreadSafeArrayAggregate implements Aggregate {private Object[] items;private int size;private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();public ThreadSafeArrayAggregate(int capacity) {items = new Object[capacity];}public void addItem(Object item) {lock.writeLock().lock();try {items[size++] = item;} finally {lock.writeLock().unlock();}}public int size() {lock.readLock().lock();try {return size;} finally {lock.readLock().unlock();}}public Object getItem(int index) {lock.readLock().lock();try {return items[index];} finally {lock.readLock().unlock();}}@Overridepublic Iterator createIterator() {return new ThreadSafeArrayIterator(this);}private class ThreadSafeArrayIterator implements Iterator {private ThreadSafeArrayAggregate aggregate;private int index = 0;public ThreadSafeArrayIterator(ThreadSafeArrayAggregate aggregate) {this.aggregate = aggregate;}@Overridepublic boolean hasNext() {lock.readLock().lock();try {return index < aggregate.size();} finally {lock.readLock().unlock();}}@Overridepublic Object next() {lock.readLock().lock();try {return aggregate.getItem(index++);} finally {lock.readLock().unlock();}}}
}
四、框架與源碼中的迭代器實踐
1. Java 集合框架(java.util
包)
- 核心接口:
java.util.Iterator
和java.util.ListIterator
- 示例:遍歷
ArrayList
import java.util.ArrayList;
import java.util.Iterator;public class JavaIteratorExample {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("A");list.add("B");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
2. Hibernate 的迭代器(ScrollableResults
)
- 用于處理大量數據查詢,避免一次性加載全部數據到內存
Session session = sessionFactory.openSession();
Query query = session.createQuery("from User");
ScrollableResults results = query.scroll();
while (results.next()) {User user = (User) results.get(0);// 處理用戶數據
}
results.close();
3. Python 中的迭代器與生成器
- 迭代器協議:通過
__iter__()
和__next__()
方法實現 - 生成器:使用
yield
關鍵字簡化迭代器實現
# 生成器函數
def fibonacci():a, b = 0, 1while True:yield aa, b = b, a + b# 使用生成器
for num in fibonacci():if num > 100:breakprint(num)
五、避坑指南:正確使用迭代器模式的 3 個要點
1. 避免在迭代過程中修改聚合對象
- ? 反模式:在迭代器遍歷過程中刪除聚合元素(可能導致
ConcurrentModificationException
) - ? 最佳實踐:使用
remove()
方法(若迭代器支持),或先記錄待刪除元素,遍歷結束后再操作。
2. 處理空聚合的邊界情況
- 確保
hasNext()
在空聚合中返回false
,next()
在空聚合或遍歷結束后拋出NoSuchElementException
。
3. 迭代器的生命周期管理
- 避免迭代器被長時間持有,導致資源無法釋放(如數據庫游標未關閉)。
- 對于一次性遍歷需求,可使用匿名內部類或局部內部類簡化代碼。
4. 反模式:過度封裝簡單遍歷
- 對于簡單的數組或集合遍歷,直接使用
for-each
循環可能更簡潔,無需引入迭代器模式。
六、總結:何時該用迭代器模式?
適用場景 | 核心特征 | 典型案例 |
---|---|---|
遍歷多種數據結構 | 數據存儲結構復雜(如樹、圖),需統一遍歷方式 | 文件系統目錄遍歷、數據庫查詢結果遍歷 |
分離數據結構與遍歷邏輯 | 避免客戶端耦合具體數據結構實現細節 | 集合框架、ORM 框架 |
支持復雜遍歷需求 | 需要實現倒序遍歷、跳躍遍歷、并發遍歷等 | 大數據處理、多線程迭代 |
迭代器模式通過 “封裝遍歷 + 解耦結構” 的設計,使數據的訪問與存儲方式分離,提升了代碼的可維護性和擴展性。下一篇我們將深入探討裝飾模式,解析如何在不修改原有類的基礎上動態添加功能,敬請期待!
擴展思考:迭代器模式 vs 枚舉(Enumeration)
模式 | 功能特性 | 線程安全 | 可修改性 | 典型應用 |
---|---|---|---|---|
迭代器模式 | 支持刪除、雙向遍歷、自定義遍歷 | 需手動實現 | 支持 | 集合框架、復雜數據結構遍歷 |
枚舉 | 僅支持單向遍歷,功能較簡單 | 部分支持 | 不支持 | 早期 Java 集合遍歷 |
理解這些差異,有助于在不同場景下選擇合適的遍歷方案。