迭代器模式(Iterator Pattern),提供了順序訪問集合中每個元素的功能,並且不用暴露其物件內部表示。
簡單來說,就是實現一個迭代功能,迭代功能,每種程式語言都有,比如for迴圈就可以直接用,那為什麼還要用迭代器模式呢?迭代器模式的一個優勢就是將集合物件的遍歷操作,從解和中拆分出來,放到一個迭代器類中,讓兩者職責更加單一,可以互不干擾的相互演變自己的功能。 並且for迴圈有個問題就是,使用for迴圈,要明確知道被遍歷的物件是什麼,只有知道了型別,才能操作獲取其中的物件。
如果是陣列,那麼就是下面這種:
int[] arrs = new int[]{1,2,3,4,5};for(int i=0;i<=arrs.length; i++) { arrs[i] ...}
如果是集合,那麼遍歷的方式,判斷集合長度,就要變成:
for(int i=0;i<=list.size(); i++) { list.get(i) ...}
因為陣列獲取元素數量和集合獲取元素數量是不同的,這就是我們上面闡述的問題,我需要明確知道被遍歷物件是什麼。
但這其實並不是主要的問題,for迴圈在遍歷資料、ArrayList這樣簡單的資料結構時,是夠用的,但是如果是遍歷複雜的資料結構,比如樹、圖,樹有前中後序、按層遍歷,圖有深度優先、廣度優先遍歷等等,這時如果你還用for迴圈遍歷,那麼這些遍歷邏輯就需要你自己來實現了,所以這時候用迭代器模式,將這些複雜的邏輯提取到一個迭代器類中,能夠讓使用者,只關注使用遍歷整個功能,而不用人人去寫一套遍歷邏輯。
所以使用迭代器,這些差異,將會被迭代器封裝,不管是陣列、ArrayList、LinkedList、Tree等等,最後呼叫迭代獲取元素的方式是統一的,從表面看是沒有差異的,這就是迭代器模式的優勢。
我們來看一下演示程式碼:
1、新建自定義抽象迭代器
/** * www.itzhimei.com * 迭代器模式--自定義抽象迭代器 */public interface IMyIterator { boolean hasNext(); Object next();}
2、新建自定義實現具體迭代器
import java.util.ArrayList;/** * www.itzhimei.com * 迭代器模式--自定義實現具體迭代器 */public class MyIterator implements IMyIterator { private ArrayList<Object> list; private int index = -1; public MyIterator(ArrayList<Object> list) { this.list = list; } /** * 判斷是否還有元素 * @return */ @Override public boolean hasNext() { return this.list == null ? false: (index < list.size()-1); } /** * 獲取元素 * @return */ @Override public Object next() { if(this.list != null && index < list.size()-1) { return this.list.get(++index); } return null; }}
3、新建自定義抽象迭代器持有類
/** * www.itzhimei.com * 迭代器模式--自定義抽象迭代器持有類 */public interface IMyList { IMyIterator iterator();}
4、新建自定義具體迭代器持有類
import java.util.ArrayList;/** * www.itzhimei.com * 迭代器模式--自定義具體迭代器持有類 */public class MyList implements IMyList{ ArrayList<Object> list; public MyList(ArrayList<Object> list) { this.list = list; } @Override public IMyIterator iterator() { return new MyIterator(this.list); }}
5、新建用於迭代的Person類
/** * www.itzhimei.com * 迭代器模式--Person類用於測試 */public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
6、測試
import java.util.ArrayList;/** * www.itzhimei.com * 測試 */public class Client { public static void main(String[] args) { Person p1 = new Person(); p1.setName("張三"); Person p2 = new Person(); p2.setName("李四"); Person p3 = new Person(); p3.setName("王五"); ArrayList ps = new ArrayList(); ps.add(p1); ps.add(p2); ps.add(p3); IMyList list = new MyList(ps); IMyIterator iterator = list.iterator(); while(iterator.hasNext()) { Object next = iterator.next(); System.out.println("迭代列印Person Name:"+((Person)next).getName()); } }}
輸出結果:
迭代列印Person Name:張三迭代列印Person Name:李四迭代列印Person Name:王五
類關係圖:
我們來簡單看一下JDK ArrayList原始碼中迭代器的實現,原始碼:
/** * An optimized version of AbstractList.Itr */private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; //這個是標記一下,當建立這個迭代器的時候,當前有多少個元素 Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); 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(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { //JDK1.8新加的lambda消費方法,忽略...... } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }}
整個class Itr,是ArrayList的一個內部類,其實現了介面Iterator,也具備了hasNext、next、remove方法。hasNext方法很簡單,就是判斷當前遊標和集合元素數量大小是否一致。重點看next方法:
public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
其中方法進來就呼叫了checkForComodification(),其作用是判斷,在建立了迭代器物件之後,其中的元素是否被改變,也就是新增或刪除了元素,如果被修改過就拋錯。ArrayList的add方法中會更新modCount的數量
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
這一行就是更新modCount的數量:ensureCapacityInternal(size + 1); // Increments modCount!!
剩下的程式碼邏輯和我們demo的邏輯是相似的,獲取元素的同時,移動遊標指標向後移動。
總結:
迭代器的優勢
1、迭代器不需要知道結合物件內部實現細節,就能夠實現集合元素的遍歷;
2、使用迭代器後,獲取元素的方式,就不再是下標,而是透過通用方式,比如這裡演示程式碼中的next()方法;你可能會說,你的next程式碼中也使用了下標獲取元素才返回的,確實如此,這是因為視角不同,我們使用迭代器模式,好處在於將集合的迭代、元素獲取,同某一個集合的具體迭代方式、使用方式解耦。
還是那句話,設計模式都是在特定的場景下,才能發揮巨大作用。