引言:集合遍歷的演進之路
在軟件開發中,集合遍歷是我們每天都要面對的基礎操作。從最初的數組索引遍歷到現代的流式處理,我們經歷了:
然而,即使有了高級API,迭代器模式仍然是理解集合遍歷本質的關鍵。它提供了一種統一的方式訪問各種聚合對象中的元素,而無需暴露底層表示。本文將深入解析迭代器模式的原理、實現及高級應用場景。
一、模式定義與核心思想
1.1 官方定義
迭代器模式 (Iterator Pattern):提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。
1.2 設計哲學
核心原則:
- 單一職責:將遍歷邏輯與集合實現分離
- 開閉原則:新增集合類型不影響遍歷代碼
- 統一接口:為不同集合提供一致的遍歷方式
二、模式結構解析
2.1 UML類圖
2.2 關鍵角色
角色 | 職責 | Java集合示例 |
---|---|---|
Iterator | 定義遍歷接口 | java.util.Iterator |
ConcreteIterator | 實現具體遍歷邏輯 | ArrayList.Itr |
Aggregate | 定義創建迭代器接口 | java.lang.Iterable |
ConcreteAggregate | 實現集合創建迭代器 | ArrayList |
三、代碼實戰:自定義集合實現
3.1 場景描述
實現一個支持多種遍歷方式的二維矩陣:
- 行優先遍歷(從左到右,從上到下)
- 列優先遍歷(從上到下,從左到右)
- 對角線遍歷
3.2 核心實現
// 迭代器接口
public interface MatrixIterator<T> {boolean hasNext();T next();
}// 矩陣接口
public interface Matrix<T> {int getRows();int getColumns();T get(int row, int col);MatrixIterator<T> rowMajorIterator();MatrixIterator<T> columnMajorIterator();MatrixIterator<T> diagonalIterator();
}// 具體矩陣實現
public class ArrayMatrix<T> implements Matrix<T> {private final T[][] data;public ArrayMatrix(T[][] data) {this.data = data;}@Overridepublic int getRows() {return data.length;}@Overridepublic int getColumns() {return data[0].length;}@Overridepublic T get(int row, int col) {return data[row][col];}// 行優先迭代器@Overridepublic MatrixIterator<T> rowMajorIterator() {return new RowMajorIterator();}// 列優先迭代器@Overridepublic MatrixIterator<T> columnMajorIterator() {return new ColumnMajorIterator();}// 對角線迭代器@Overridepublic MatrixIterator<T> diagonalIterator() {return new DiagonalIterator();}// 行優先迭代器實現private class RowMajorIterator implements MatrixIterator<T> {private int row = 0;private int col = 0;@Overridepublic boolean hasNext() {return row < getRows() && col < getColumns();}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();T element = get(row, col);col++;if (col >= getColumns()) {col = 0;row++;}return element;}}// 列優先迭代器實現private class ColumnMajorIterator implements MatrixIterator<T> {private int row = 0;private int col = 0;@Overridepublic boolean hasNext() {return col < getColumns() && row < getRows();}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();T element = get(row, col);row++;if (row >= getRows()) {row = 0;col++;}return element;}}// 對角線迭代器實現private class DiagonalIterator implements MatrixIterator<T> {private int diag = 0;private int pos = 0;@Overridepublic boolean hasNext() {return diag < (getRows() + getColumns() - 1);}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();int row, col;if (diag < getRows()) {row = diag - pos;col = pos;} else {row = getRows() - 1 - pos;col = diag - getRows() + 1 + pos;}T element = get(row, col);pos++;if (pos > diag || (diag >= getRows() && pos >= getRows() + getColumns() - diag - 1)) {diag++;pos = 0;}return element;}}
}
3.3 客戶端使用
public class MatrixClient {public static void main(String[] args) {Integer[][] matrixData = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};Matrix<Integer> matrix = new ArrayMatrix<>(matrixData);System.out.println("行優先遍歷:");printIterator(matrix.rowMajorIterator());System.out.println("\n列優先遍歷:");printIterator(matrix.columnMajorIterator());System.out.println("\n對角線遍歷:");printIterator(matrix.diagonalIterator());}private static void printIterator(MatrixIterator<?> iterator) {while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();}
}/* 輸出:
行優先遍歷:
1 2 3 4 5 6 7 8 9 列優先遍歷:
1 4 7 2 5 8 3 6 9 對角線遍歷:
1 2 4 3 5 7 6 8 9
*/
3.4 遍歷過程可視化
四、迭代器模式進階技巧
4.1 線程安全迭代器
public class SafeIterator<T> implements Iterator<T> {private final Object lock = new Object();private final Iterator<T> delegate;public SafeIterator(Iterator<T> delegate) {this.delegate = delegate;}@Overridepublic boolean hasNext() {synchronized (lock) {return delegate.hasNext();}}@Overridepublic T next() {synchronized (lock) {return delegate.next();}}@Overridepublic void remove() {synchronized (lock) {delegate.remove();}}
}
4.2 過濾迭代器
public class FilterIterator<T> implements Iterator<T> {private final Iterator<T> source;private final Predicate<T> filter;private T nextElement;private boolean hasNext;public FilterIterator(Iterator<T> source, Predicate<T> filter) {this.source = source;this.filter = filter;findNext();}private void findNext() {while (source.hasNext()) {T candidate = source.next();if (filter.test(candidate)) {nextElement = candidate;hasNext = true;return;}}hasNext = false;}@Overridepublic boolean hasNext() {return hasNext;}@Overridepublic T next() {if (!hasNext) throw new NoSuchElementException();T result = nextElement;findNext();return result;}
}// 使用示例
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Iterator<Integer> evenIterator = new FilterIterator<>(numbers.iterator(), n -> n % 2 == 0
);
4.3 分頁迭代器
public class PagingIterator<T> implements Iterator<List<T>> {private final Iterator<T> source;private final int pageSize;public PagingIterator(Iterator<T> source, int pageSize) {this.source = source;this.pageSize = pageSize;}@Overridepublic boolean hasNext() {return source.hasNext();}@Overridepublic List<T> next() {if (!hasNext()) throw new NoSuchElementException();List<T> page = new ArrayList<>(pageSize);for (int i = 0; i < pageSize && source.hasNext(); i++) {page.add(source.next());}return page;}
}// 使用示例
List<Integer> bigList = IntStream.range(0, 1000).boxed().collect(Collectors.toList());
Iterator<List<Integer>> pageIterator = new PagingIterator<>(bigList.iterator(), 100);while (pageIterator.hasNext()) {List<Integer> page = pageIterator.next();processPage(page);
}
五、應用場景分析
5.1 典型應用場景
場景 | 迭代器應用 | 優勢 |
---|---|---|
集合框架 | Java集合遍歷 | 統一訪問接口 |
文件系統 | 目錄遍歷 | 隱藏文件系統差異 |
數據庫 | 結果集遍歷 | 抽象數據庫訪問 |
樹形結構 | 多種遍歷算法 | 解耦結構與遍歷 |
流處理 | 數據管道 | 支持鏈式操作 |
5.2 使用時機判斷
當出現以下情況時考慮迭代器模式:
- 需要統一訪問不同聚合結構
- 需要支持多種遍歷方式
- 需要隱藏聚合的內部結構
- 需要為聚合提供遍歷接口
5.3 不適用場景
- 簡單數組或列表遍歷
- 性能極度敏感的場景
- 只需要順序訪問的不可變集合
六、模式優劣辯證
6.1 優勢 ?
6.2 劣勢 ?
- 性能開銷:比直接索引訪問慢
- 復雜性增加:簡單場景過度設計
- 并發修改問題:迭代中修改集合導致異常
- 單向遍歷限制:標準迭代器只支持單向移動
七、與其他模式的關系
7.1 迭代器模式 vs 訪問者模式
- 協同關系:迭代器負責遍歷,訪問者負責操作
- 區別:
- 迭代器:關注元素訪問順序
- 訪問者:關注元素操作邏輯
7.2 迭代器模式 vs 組合模式
維度 | 迭代器模式 | 組合模式 |
---|---|---|
目的 | 遍歷元素 | 構建樹形結構 |
關注點 | 訪問順序 | 結構組織 |
典型配合 | 常遍歷組合結構 | 常提供迭代器 |
八、在Java集合框架中的應用
8.1 迭代器接口演進
8.2 ArrayList迭代器實現
// ArrayList內部迭代器
private class Itr implements Iterator<E> {int cursor; // 下一個元素索引int lastRet = -1; // 最后返回的元素索引int expectedModCount = modCount; // 并發修改檢查public boolean hasNext() {return cursor != size;}public E next() {checkForComodification();int i = cursor;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();}
}
8.3 Java 8 Spliterator
public interface Spliterator<T> {boolean tryAdvance(Consumer<? super T> action);Spliterator<T> trySplit();long estimateSize();int characteristics();
}// 使用示例
List<String> list = Arrays.asList("a", "b", "c");
Spliterator<String> spliterator = list.spliterator();// 順序處理
spliterator.forEachRemaining(System.out::println);// 并行處理
spliterator.trySplit().forEachRemaining(System.out::println);
九、最佳實踐指南
9.1 設計建議
-
使用Iterator而不是直接訪問
// 好 for (Iterator<Item> it = collection.iterator(); it.hasNext(); ) {Item item = it.next();// ... }// 更好 (Java 5+) for (Item item : collection) {// ... }
-
支持fail-fast機制
public class SafeIterable<T> implements Iterable<T> {private final List<T> data = new ArrayList<>();private volatile int modCount = 0;@Overridepublic Iterator<T> iterator() {return new SafeIterator<>(data.iterator(), modCount);}private class SafeIterator implements Iterator<T> {private final Iterator<T> delegate;private final int expectedModCount;public SafeIterator(Iterator<T> delegate, int modCount) {this.delegate = delegate;this.expectedModCount = modCount;}private void checkModification() {if (modCount != expectedModCount) {throw new ConcurrentModificationException();}}@Overridepublic boolean hasNext() {checkModification();return delegate.hasNext();}// next() 和 remove() 類似} }
9.2 性能優化
-
隨機訪問優化
public class RandomAccessIterator<T> implements Iterator<T> {private final RandomAccess randomAccess;private final int size;private int index = 0;public RandomAccessIterator(List<T> list) {if (!(list instanceof RandomAccess)) {throw new IllegalArgumentException("List must support random access");}this.randomAccess = (RandomAccess) list;this.size = list.size();}@Overridepublic boolean hasNext() {return index < size;}@Overridepublic T next() {return ((List<T>) randomAccess).get(index++);} }
-
延遲加載迭代器
public class LazyIterator<T> implements Iterator<T> {private final Supplier<T> supplier;private T next;public LazyIterator(Supplier<T> supplier) {this.supplier = supplier;advance();}private void advance() {next = supplier.get();}@Overridepublic boolean hasNext() {return next != null;}@Overridepublic T next() {if (next == null) throw new NoSuchElementException();T result = next;advance();return result;} }
9.3 遍歷策略對比
策略 | 時間復雜度 | 空間復雜度 | 適用場景 |
---|---|---|---|
順序迭代 | O(n) | O(1) | 鏈表、文件流 |
隨機訪問 | O(1) | O(1) | 數組、ArrayList |
分頁迭代 | O(n) | O(pageSize) | 大數據集 |
并行迭代 | O(n/p) | O§ | 多核處理器 |
十、總結:迭代器模式的核心價值
迭代器模式通過解耦遍歷實現了:
設計啟示:
將遍歷視為獨立于集合結構的操作,讓集合專注存儲,迭代器專注訪問
正如《設計模式》作者GoF所強調:
“迭代器模式提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露其內部的表示”
擴展思考:
- 如何在分布式系統中實現迭代器?
- 迭代器模式如何與響應式編程結合?
- 如何設計支持雙向遍歷的迭代器?
附錄:Java迭代器發展史
版本 | 特性 | 代表類/接口 |
---|---|---|
JDK 1.0 | 基本枚舉 | Enumeration |
JDK 1.2 | 標準迭代器 | Iterator |
JDK 1.5 | 增強for循環 | Iterable |
JDK 1.5 | 雙向迭代 | ListIterator |
JDK 8 | 并行迭代 | Spliterator |
JDK 8 | 流式迭代 | Stream |
迭代器模式是構建靈活、可擴展集合框架的基石,其設計思想深刻影響了現代編程語言的數據處理范式。