原文:http://blog.csdn.net/mudalu626/article/details/6457427
一、 引言
迭代這個名詞對於熟悉Java的人來說絕對不陌生。我們常常使用JDK提供的迭代接口進行java collection的遍歷:
Iterator it = list.iterator();
while(it.hasNext()){
//using “it.next();”do some businesss logic
}
而這就是關於迭代器模式應用很好的例子。
二、 定義與結構
迭代器(Iterator)模式,又叫做遊標(Cursor)模式。GOF給出的定義爲:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。
從定義可見,迭代器模式是爲容器而生。很明顯,對容器對象的訪問必然涉及到遍歷算法。你可以一股腦的將遍歷方法塞到容器對象中去;或者根本不去提供什麼遍歷算法,讓使用容器的人自己去實現去吧。這兩種情況好像都能夠解決問題。
然而在前一種情況,容器承受了過多的功能,它不僅要負責自己“容器”內的元素維護(添加、刪除等等),而且還要提供遍歷自身的接口;而且由於遍歷狀態保存的問題,不能對同一個容器對象同時進行多個遍歷。第二種方式倒是省事,卻又將容器的內部細節暴露無遺。
而迭代器模式的出現,很好的解決了上面兩種情況的弊端。先來看下迭代器模式的真面目吧。
迭代器模式由以下角色組成:
1) 迭代器角色(Iterator):迭代器角色負責定義訪問和遍歷元素的接口。
2) 具體迭代器角色(Concrete Iterator):具體迭代器角色要實現迭代器接口,並要記錄遍歷中的當前位置。
3) 容器角色(Container):容器角色負責提供創建具體迭代器角色的接口。
4) 具體容器角色(Concrete Container):具體容器角色實現創建具體迭代器角色的接口——這個具體迭代器角色於該容器的結構相關。
迭代器模式的類圖如下:
三、 從Java Collections源碼分析迭代器模式
下面我們拿出java中Iterator的源碼相關源碼進行分析。
下圖是Java Collections的整體關係圖:
從圖中很容易看出,Collection和代表相應的抽象容器角色,而Iterator和ListIterator則代表相應的抽象迭代器角色。
其中ListIterator是Iterator的子接口,擴充了向後遍歷的方法接口。
以下是Collection,Iterator,List及ListIterator的代碼片段:
Collection:
- //Collection繼承自Iterable接口,而Iterable就是用來生產Iterator的接口
- public interface Collection<E> extends Iterable<E> {
- ...
- //繼承自Iterable的方法,實現Collection和Iterator的耦合
- Iterator<E> iterator();
- ...
- }
Iterator:
- public interface Iterator<E> {
- boolean hasNext();//如果仍有元素可以迭代,則返回 true。
- E next();//返回迭代的下一個元素。
- void remove();//從迭代器指向的集合中移除迭代器返回的最後一個元素(可選操作)。
- }
List:
- //List繼承自Collection
- public interface List<E> extends Collection<E> {
- Iterator<E> iterator();//List和Iterator的耦合
- ListIterator<E> listIterator();//List和ListIterator的耦合
- ListIterator<E> listIterator(int index);//從列表的指定位置開始
- }
ListIterator:
- //ListIterator繼承自Iterator
- public interface ListIterator<E> extends Iterator<E> {
- boolean hasNext();//以正向遍歷列表時,如果列表迭代器有多個元素,則返回 true(換句話說,如果 next 返回一個元素而不是拋出異常,則返回 true)
- E next();//返回列表中的下一個元素
- boolean hasPrevious();//如果以反向遍歷列表,列表迭代器有多個元素,則返回 true
- E previous();//返回列表中的前一個元素
- int nextIndex();//返回對 next 的後續調用所返回元素的索引
- int previousIndex();//返回對 previous 的後續調用所返回元素的索引
- void remove();//從列表中移除由 next 或 previous 返回的最後一個元素(可選操作)
- void set(E e);//用指定元素替換 next 或 previous 返回的最後一個元素(可選操作)
- void add(E e);//將指定的元素插入列表(可選操作)
- }
那麼具體容器角色和具體迭代器角色是那些類呢?
首先先從Iterator入手,讓我們先回到圖上,熟悉Java Collections框架的朋友很容易明白Collection的實現類很多,它是Java Collections框架的頂層結構,它的下層又分Set和List兩個分支,那我們就分別來分析這兩個分支具體的具體實現。
List的第一級實現類爲AbstractList,java在此類中實現了具體容器角色和具體迭代器角色,那如何能在一個類中實現兩個角色呢,還是從代碼入手吧:
- //AbstractList實現類List接口
- public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
- ...
- //已從結構上修改 此列表的次數。從結構上修改是指更改列表的大小,或者以其他方式打亂列表,使正在進行的迭代產生錯誤的結果
- protected transient int modCount = 0;
- ...
- //生產迭代器,每次生產一個新的對象,所以對同一個容器對象,可以同時進行多個遍歷
- public Iterator<E> iterator() {
- return new Itr();
- }
- ...
- //這就是解決一個類實現兩個角色的關鍵,用了一個私有內部類來實現了Iterator
- private class Itr implements Iterator<E> {
- //遊標,也就是被遍歷具體容器(AbstractList)的當前索引值
- int cursor = 0;
- //離遊標最近的索引值,主要用在remove方法中,下面會詳細說明
- int lastRet = -1;
- //此變量用作跟蹤具體容器(AbstractList)的modCount變量,如果不一致則報錯
- int expectedModCount = modCount;
- //原來hasNext的實現如此簡單,用遊標和具體容器的size進行比較即可,遊標的操作見next方法
- public boolean hasNext() {
- return cursor != size();
- }
- //next方法也不難嘛,就是利用遊標一次一次對具體容器進行遍歷
- public E next() {
- //檢查迭代器運行期間,容器結構是否修改,如修改則報錯
- checkForComodification();
- try {
- //呵呵,終於找到你了,原來又是用具體容器的方法實現的,這就是隱藏容器細節的關鍵
- E next = get(cursor);
- //記錄被返回的索引位置,並使遊標向前移動
- lastRet = cursor++;
- //返回被迭代的當前對象
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- //不用說了,remove方法的實現肯定也是借了容器之力
- public void remove() {
- if (lastRet == -1)
- throw new IllegalStateException();
- checkForComodification();
- try {
- //嘿嘿,又是隱藏細節
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- //因爲remove方法改變了容器結構,所以需要同步一下expectedModCount變量使修改次數一致
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
- //檢查迭代器運行期間,容器結構是否修改,如修改則報錯
- final void checkForComodification() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- }
- }
- ...
- //同理,ListIterator的實現類也作爲容器的私有內部類出現
- private class ListItr extends Itr implements ListIterator<E> {
- //具體實現就不介紹了,和Iterator大同小異
- ListItr(int index) {
- cursor = index;
- }
- public boolean hasPrevious() {
- return cursor != 0;
- }
- public E previous() {
- checkForComodification();
- try {
- int i = cursor - 1;
- E previous = get(i);
- lastRet = cursor = i;
- return previous;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public int nextIndex() {
- return cursor;
- }
- public int previousIndex() {
- return cursor-1;
- }
- public void set(E e) {
- if (lastRet == -1)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.set(lastRet, e);
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException ex) {
- throw new ConcurrentModificationException();
- }
- }
- public void add(E e) {
- checkForComodification();
- try {
- AbstractList.this.add(cursor++, e);
- lastRet = -1;
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException ex) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
對於再下層的實現類,可以靈活的重寫生產方法,並實現專用的迭代器內部類。
Set和List不同,它的下一層沒有實現類,而是倆個接口(AbstractSet和SortedSet),再往下一層找便是我們熟悉的兩個實現類HashSet和TreeSet,而瞭解Java Collection的讀者會知道,HashSet和TreeSet其實是藉助Map系的HashMap和TreeMap實現的,所以具體實現方法是在Map系中的實現類中實現的(隱藏夠深的)。因爲涉及的兩個對象數據結構稍微有些複雜(散列表和紅黑樹),可以在此處瞭解其內部構造。廢話少說,上代碼:
- public class HashMap<K,V>
- extends AbstractMap<K,V>
- implements Map<K,V>, Cloneable, Serializable
- {
- ...
- //已從結構上修改 此列表的次數。
- transient volatile int modCount;
- ...
- //因散列表結構的特殊性,需要分別實現三個迭代器KeyIterator、ValueIterator和EntryIterator,基類爲HashIterator
- //基類爲HashIterator
- private abstract class HashIterator<E> implements Iterator<E> {
- Entry<K,V> next; // 散列表的鏈表的下一個Entry對象
- int expectedModCount; // 與modCount同步的變量
- int index; // 散列表的數組索引
- Entry<K,V> current; // 散列表的鏈表的當前Entry對象
- //構造函數
- HashIterator() {
- //同步expectedModCount
- expectedModCount = modCount;
- if (size > 0) {
- //取得散列表的數組
- Entry[] t = table;
- //取得取得散列表的數組中不爲空的項目索引並將數組中的鏈表頭賦值給next
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- }
- //雖然沒看到對HashMap的方法調用,但是在構造函數中已經和HashMap對象通信
- public final boolean hasNext() {
- return next != null;
- }
- //通過分別遍歷散列表中的當前鏈表和數組,查找下一個Entry對象
- final Entry<K,V> nextEntry() {
- //檢查迭代器運行期間,容器結構是否修改,如修改則報錯
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- //取得遍歷散列表中的當前鏈表
- Entry<K,V> e = next;
- if (e == null)
- throw new NoSuchElementException();
- //如果當前鏈表遍歷到尾,則繼續對數組進行遍歷
- if ((next = e.next) == null) {
- Entry[] t = table;
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- //取得最新的鏈表
- current = e;
- //返回Entry對象
- return e;
- }
- //使用了HashMap對象的removeEntryForKey進行刪除操作,又是隱藏細節。。。
- public void remove() {
- if (current == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- Object k = current.key;
- current = null;
- HashMap.this.removeEntryForKey(k);
- expectedModCount = modCount;
- }
- }
- //迭代器的具體實現類ValueIterator,繼承自HashIterator
- private final class ValueIterator extends HashIterator<V> {
- //實現next方法,返回Entry對象的Value
- public V next() {
- return nextEntry().value;
- }
- }
- //迭代器的具體實現類KeyIterator,繼承自HashIterator
- private final class KeyIterator extends HashIterator<K> {
- //實現next方法,返回Entry對象的Key
- public K next() {
- return nextEntry().getKey();
- }
- }
- //迭代器的具體實現類EntryIterator,繼承自HashIterator
- private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
- //實現next方法,返回Entry對象
- public Map.Entry<K,V> next() {
- return nextEntry();
- }
- }
- //三個工廠方法分別製造三個不同的迭代器
- Iterator<K> newKeyIterator() {
- return new KeyIterator();
- }
- Iterator<V> newValueIterator() {
- return new ValueIterator();
- }
- Iterator<Map.Entry<K,V>> newEntryIterator() {
- return new EntryIterator();
- }
- //因上面三個迭代器爲HashMap專用,所以不適合Set(ValueIterator和EntryIterator),所以在HashMap的內部類KeySet中又使用了HashMap的KeyIterator,所以HashSet的具體迭代器其實就是HashMap的KeyIterator迭代器
- private final class KeySet extends AbstractSet<K> {
- public Iterator<K> iterator() {
- return newKeyIterator();
- }
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsKey(o);
- }
- public boolean remove(Object o) {
- return HashMap.this.removeEntryForKey(o) != null;
- }
- public void clear() {
- HashMap.this.clear();
- }
- }
- private transient Set<Map.Entry<K,V>> entrySet = null;
- public Set<K> keySet() {
- Set<K> ks = keySet;
- return (ks != null ? ks : (keySet = new KeySet()));
- }
- }
TreeSet的迭代器實現和HashMap很相似,在這裏就不再羅嗦了,有興趣的讀者可以去研讀一下源碼。
對於再下層的實現類,可以靈活的重寫生產方法,並實現專用的迭代器內部類。
三、 總結
從代碼我們可以得出以下幾點:
1.具體容器角色和具體迭代器角色因爲是緊耦合關係,在具體容器中使用內部類將具體迭代器實現是一個很好的做法,方便了具體迭代器直接訪問具體容器的所有細節。
2.內部類爲私有,既實現類具體迭代器,又使此容器的專用迭代器對外不可見,迫使使用者進行接口編程。
3.使用迭代器訪問一個容器對象的內容而無需暴露它的內部表示。
4.對同一個容器對象,可以同時進行多個遍歷。
5.支持以不同的方式遍歷一個容器角色。根據實現方式的不同,效果上會有差別。
6.簡化了容器的接口。但是在java Collection中爲了提高可擴展性,容器還是提供了遍歷的接口。
7.爲遍歷不同的容器結構提供一個統一的接口。