首頁>技術>

迭代器模式(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程式碼中也使用了下標獲取元素才返回的,確實如此,這是因為視角不同,我們使用迭代器模式,好處在於將集合的迭代、元素獲取,同某一個集合的具體迭代方式、使用方式解耦。

還是那句話,設計模式都是在特定的場景下,才能發揮巨大作用。

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • C/C++程式設計筆記:C++中的I / O重定向