迭代器模式,Iterator,java集合框架內置的一種模式,本文介紹了迭代器模式的起源含義,設計意圖,以及結構形態,並且給出了Java版本的迭代器模式的實現,迭代器模式分為內部迭代和外部迭代,Java集合框架使用的這種形式是比較好的一種方式。 ...
迭代器模式(Iterator) 走遍天下,世界那麼大,我想去看看 在電腦中,Iterator意為迭代器,迭代有重覆的含義,在程式中,更有“遍歷”的含義 如果給定一個數組,我們可以通過for迴圈來遍歷這個數組,這種遍歷就叫做迭代 對於數組這種數據結構,我們稱為是可迭代的 所以 迭代器就是可以用來對於一個數據集合進行遍歷的對象
意圖
提供一種方法,順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示。 別名:游標 Cursor集合與遍歷
由一個或多個確定的元素所構成的整體叫做集合。 多個對象聚集在一起形成的總體稱之為聚集aggregate。 集合和聚集有相似的含義。 容器是指盛物品的器具,Java的Collection框架,就是設計用來保存對象的容器 容器可以認為是集合、聚集的具體體現形式,三個元素在一起叫做集合(聚集),怎麼在一起?數組,列表?這具體的體現形式就是容器 容器必須提供內部對象的訪問方式,如果不能獲取對象,容器也失去了存在的意義,一個只能進不能出的儲蓄罐你要它何用? 因為容器的存在就是為了更方便的使用、管理對象。 而且通常需要容器提供對於內部所有元素的遍歷方法。 然而容器其內部有不同的擺放形式,可順序,可無序堆集 簡言之,就是不同類型的容器必然有不同的內部數據結構 那麼,一種解決辦法就是不同的容器各自提供自己的遍歷方法。 這樣的話,對於使用容器管理對象的客戶端程式來說: 如果迭代的邏輯,也就是業務邏輯沒有變化 當需要更換為另外的集合時,就需要同時更換這個迭代方法 考慮這樣一個場景有一個方法,方法的參數類型為 Collection
他的迭代邏輯,也就是業務邏輯為遍歷所有元素,讀取每個元素的信息,並且進行列印...
如果不同的容器有不同的遍歷方法,也就是一種實現類有一種不同的遍歷方法
一旦更換實現類,那麼就需要同步更換掉這個迭代方法,否則方法將無法通過編譯 如果集合的實現不變,需要改變業務邏輯,也就是迭代的邏輯 那麼就需要修改容器類的迭代方法,也就是修改原來的遍歷方法 還是上面的場景 有一個方法,方法的參數類型為 Collection 他的迭代邏輯,也就是業務邏輯為遍歷所有元素,讀取每個元素的信息,並且進行列印... 現在他的實現類無需變化 但是業務邏輯需要變動,比如希望從後往前的方式進行遍歷,而不再是從前往後 就需要修改原來的方法或者重新寫一個方法 出現上述問題的根本原因就在於元素的迭代邏輯與容器本身耦合在一起 當迭代邏輯或者集合實現發生變更時,需要進行修改,不符合開閉原則 容器自身不僅僅需要存儲管理對象,還要負責對象的遍歷訪問,不符合單一職責原則 存儲管理對象是容器的核心職責,雖然經常需要提供遍歷方法,但是他並不是核心職責 但是為了提供遍歷元素的方法,可能不得不在容器類內提供各種全局變數,比如保存當前的位置等,這無疑也會導致容器聚集類設計的複雜度
結構
抽象迭代器角色Iterator 定義遍歷元素所需要的介面 具體的迭代器ConcreteIterator 實現了Iterator介面,並且跟蹤當前位置 抽象集合容器角色Aggregate 定義創建相應迭代器的介面(方法) 就是一個容器類,並且定義了一個返回迭代器的方法 具體的容器角色ConcreteAggregate Aggregate的子類,並且實現了創建Iterator對象的介面,也就是返回一個ConcreteIterator實例 客戶端角色Client 持有容器對象以及迭代器對象的引用,調用迭代對象的迭代方法遍歷元素 迭代器模式中,通過一個外部的迭代器來對容器集合對象進行遍歷。 迭代器定義了遍歷訪問元素的協議方式。 容器集合對象提供創建迭代器的方法。示例代碼
Aggregate角色 提供了iterator()獲取Iteratorpackage iterator; public abstract class Aggregate { abstract Iterator iterator(); abstract Object get(int index); abstract int size(); }ConcreateAggregate角色 內部使用一個Object數組,數組直接通過構造方法傳遞進去(只是為了演示學習模式,不要糾結這算不上一個容器) 提供了大小的獲取方法以及獲取指定下標元素的方法 尤其是實現了iterator()方法,創建一個ConcreteIterator實例,將當前ConcreteAggregate作為參數傳遞給他的構造方法
package iterator; public class ConcreateAggregate extends Aggregate { private Object[] objects; ConcreateAggregate(Object[] objects) { this.objects = objects; } @Override Iterator iterator() { return new ConcreateIterator(this); } @Override Object get(int index) { return objects[index]; } @Override int size() { return objects.length; } }迭代器介面 一個是否還有元素的方法,一個獲取下一個元素的方法
package iterator; public interface Iterator { boolean hasNext(); Object next(); }具體的迭代器 內部維護了數據的大小和當前位置 如果下標未到最後,那麼就是還有元素 next()方法用於獲取當前元素,獲取後當前位置往後移動一下
package iterator; public class ConcreateIterator implements Iterator { private Aggregate aggregate; private int index = 0; private int size = 0; ConcreateIterator(Aggregate aggregate) { this.aggregate = aggregate; size = aggregate.size(); } @Override public boolean hasNext() { return index < size ? true : false; } @Override public Object next() { Object value = aggregate.get(index); index++; return value; } }測試類
package iterator; public class Client { public static void main(String[] args) { Object[] objects = {"1", 2, 3, 4, 5}; Aggregate aggregate = new ConcreateAggregate(objects); Iterator iterator = aggregate.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }示例代碼中ConcreateAggregate本身提供了獲取指定下標元素的方法,可以直接調用獲取元素 藉助於Iterator,將迭代邏輯從Aggregate中剝離出來,獨立封裝實現 在客戶端與容器之間,增加了一層Iterator,實現了客戶端程式與容器的解耦 說白了,增加了Iterator,相當於通過Iterator封裝了真實容器對象的獲取元素的方法 不直接調用方法,經過Iterator轉換一層 而且仔細品味下,這有點“適配”的韻味,適配的目標就是統一的元素訪問協議,通過Iterator約定 而被適配的角色,則是真實容器對象元素的操作方法 總之“間接”“委托”“代理”的感覺,對吧,好處自己品味
外部迭代與內部迭代
在上面的示常式序中,通過引入Iterator,實現了迭代邏輯的封裝抽象 但是容器聚集對象本身有獲取元素的方法,所以客戶端仍舊可以自行遍歷 Iterator也只不過是容器聚集對象的一個客戶而已 這種迭代器也叫做外部迭代器 對於外部迭代器有一個問題,對於不同的ConcreteAggregate,可能都需要一個不同的ConcreteIterator 也就是很可能會不得不創建了一個與Aggregate等級結構平行的Iterator結構,出現了很多的ConcreteIterator類 這勢必會增加維護成本 而且,雖然迭代器將客戶端的訪問與容器進行解耦,但是迭代器卻是必須依賴容器對象的 也就是迭代器類ConcreteIterator與ConcreteAggregate必須進行通信,會增加設計的複雜度,而且這也會增加類之間的耦合性 另外的一種方法是使用內部類的形式,也就是將ConcreteIterator的實現,移入到ConcreteAggregate的內部 藉助於內部類的優勢:對外部類有充足的訪問許可權,也就是無需擔心為了通信要增加複雜度的問題 準確的說,你沒有任何的通信成本,內部類可以直接讀取外部類的屬性數據信息 而且,使用內部類的方式不會導致類的爆炸(儘管仍舊是會有另一個class文件,但是從代碼維護的角度看算是一個類) 這種形式可以叫做內部迭代器 不過無論哪種方式,你可以看得出來,使用迭代器的客戶端代碼,都是一樣的 藉助於工廠方法iterator()獲得一個迭代器實例(簡單工廠模式) 然後藉助於迭代器進行元素遍歷JDK中的迭代
我們看下JDK中的Collection提供給我們的迭代方式 Collection是所有集合的父類,Collection實現了Iterable介面 Iterable介面提供了iterator()方法用於返回一個Iterator類的一個實例對象 Iterator類提供了對元素的遍歷方法 接下來看下ArrayList的實現 ArrayList中iterator()返回了一個Itr對象,而這個對象是ArrayList的內部類,實現了Iterator介面 看得出來,java給集合框架內置了迭代器模式 在ArrayList中使用就是內部類的形式,也就是內部迭代器 boolean hasNext() 是否擁有更多元素,換句話說,如果next()方法不會拋出異常,就會返回true next(); 返回下一個元素 remove() 刪除元素 有幾點需要註意 1.)初始時,可以認為“當前位置”為第一個元素前面 所以next()獲取第一個元素 2.)根據第一點,初始的當前位置”為第一個元素前面,所以如果想要刪除第一個元素的話,必須先next,然後removeIterator iterator = list.iterator();
iterator.next();
iterator.remove();
否則,會拋出異常 3.)不僅僅是刪除第一個元素需要先next,然後才能remove,每一個remove,前面必須有一個next,成對出現 所以remove是刪除當前元素 如果下麵這樣,會拋出異常
iterator.next();
iterator.remove();
iterator.remove();
4.)迭代器只能遍歷一次,如果需要重新遍歷,可以重新獲取迭代器對象 如果已經遍歷到尾部之後仍舊繼續使用,將會拋出異常
Iterator iterator = list.iterator(); while (iterator.hasNext()) { iterator.next(); } iterator.next();