文章目錄
- 簡介
- 問題
- 解決
- 代碼
- 設計關鍵點:
- 總結
簡介
迭代器是一種行為設計模式,讓你能在不暴露集合底層表現形式(列表、棧和樹等)的情況下遍歷集合中所有的元素。
問題
集合是編程中最常使用的數據類型之一。 大部分集合使用簡單列表存儲元素。但有些集合還會使用棧、樹、 圖 和其他復雜的數據結構。
無論集合的構成方式是咋樣的,它都必須提供某種訪問元素的方式,便于 其他代碼使用其中的元素。 集合應提供一種能夠遍歷元素的方式, 并且保證它不會重復地訪問同一個元素。
如果你的集合基于列表, 遍歷就會比較簡單。但怎么遍歷復雜數據結構(例如樹)中的元素呢? 比如 今天你需要使用深度 優先算法來遍歷樹結構, 明天可能會需要廣度優先算法; 下周則可能會需要其他方式(比如隨機存取樹中的元素)。 如下圖。
這個時候,你可能不斷往集合里添加遍歷算法,這會讓集合本身的職責(“高效存儲數據” )變得模糊,不符合單一職責原則。另外 有些算法可能是根據特定應用訂制的, 把他加到泛型集合類中會 顯得非常奇怪。
另一方面, 使用多種集合的客戶端代碼可能并不關心存儲數據的方式,不過由于集合提供不同的元素訪問方式, 你的代碼就不得不跟特定集合類進行耦合。
解決
迭代器模式的主要思想是將集合的遍歷行為抽取成單獨的迭代器對象。
除實現自身算法外, 迭代器還封裝了遍歷操作的所有細節, 比如當前 位置和末尾剩余元素的數量。 因此,多個迭代器可以在相互獨立的情況下同時訪問集合。
迭代器通常會提供一個獲取集合元素的基本方法。 客戶端可以不斷調用這個方法直到它不返回任何內容, 也就是說迭代器已經遍歷了所有元素。
所有迭代器必須實現相同的接口。這樣一來,只要有合適的迭代器,客戶端代碼就能兼容任何類型的集合或遍歷算法。 如果你需要采用特殊方式來遍歷集合, 只需創建一個新的迭代器類即可, 不需要對集合或客戶端進行修改。
代碼
// 1.Iterator接口 (定義遍歷標準)
interface TreeIterator<T> {boolean hasNext();T next();
}// 2.Concrete Iterator(實現廣度優先遍歷)
class BfsIterator implements TreeIterator<TreeNode> {private Queue<TreeNode> queue = new LinkedList<>();public BfsIterator(TreeNode root) {if(root != null) queue.add(root);}public boolean hasNext() { // 判斷是否有下一節點return !queue.isEmpty();}public TreeNode next() { // 按層遍歷樹節點TreeNode current = queue.poll();for(TreeNode child : current.getChildren()) {queue.add(child);}return current;}
}// 3.Collection接口(樹結構抽象)
interface TreeCollection {TreeIterator<TreeNode> createBfsIterator(); // 創建特定迭代器
}// 4.Concrete Collection(樹節點實現)
class TreeNode implements TreeCollection {private String data;private List<TreeNode> children = new ArrayList<>();public TreeNode(String data) { this.data = data; }public void addChild(TreeNode node) {children.add(node);}public List<TreeNode> getChildren() { return children; }public TreeIterator<TreeNode> createBfsIterator() {return new BfsIterator(this); // 自身作為遍歷起點}
}// 5.Client使用示例
public class TreeClient {public static void main(String[] args) {/* 構建樹結構:A/ \B C/ \D E*/TreeNode root = new TreeNode("A");TreeNode nodeB = new TreeNode("B");TreeNode nodeC = new TreeNode("C");root.addChild(nodeB);root.addChild(nodeC);nodeC.addChild(new TreeNode("D"));nodeC.addChild(new TreeNode("E"));// 獲取BFS迭代器TreeIterator<TreeNode> iterator = root.createBfsIterator();// 遍歷樹節點while(iterator.hasNext()) {System.out.println(iterator.next().data); // 依次輸出 A → B → C → D → E}}
}
設計關鍵點:
- 解耦遍歷算法:BfsIterator封裝廣度優先的具體邏輯
- 擴展性強:新增DfsIterator無需修改樹結構代碼
- 多態迭代:客戶端通過統一接口操作不同遍歷方式
總結
- (Iterator)接口聲明了遍歷集合所需的操作:獲取下一個元素、 獲取當前位置和重新開始迭代等。
- (Concrete Iterators)實現遍歷集合的一種特定算法。迭代器對象必須跟蹤自身遍歷的進度。這就讓多個迭代器可以相互獨立地遍歷同一集合。
- (Collection) 接口聲明一個或多個方法來獲取與集合兼容的迭代器。注意,方法返回的類型必須被聲明成迭代器接口, 這樣具體集合可以返回各種不同種類的迭代器。
- (Concrete Collections)會在客戶端請求迭代器時返回一個特定的具體迭代器類實體。
- (Client) 通過集合和迭代器的接口來和這兩者進行交互。 這樣一來,客戶端不需要跟具體類耦合,允許同一客戶端代碼使用各種不同的集合和迭代器。 客戶端通常不會自行創建迭代器,而是會從集合里獲取。但在特定情況下,客戶端可以直接創建一個迭代器(比如當客戶端需要自定義特殊迭代器時)。