1. 程式人生 > >Java叠代器Iterator

Java叠代器Iterator

java

之前我們實現了叠代器模式,很多編程語言實際上已經內置了叠代器類,比如Java就為我們實現了叠代器Iterator。我們首先來看Iterator中的源碼。

通過JDK源碼我們發現Iterator是一個接口,包含三個方法:hasNext、next、remove。

技術分享

 1 package java.util; 2  3 public interface Iterator<E> { 4  5     /** 6     *如果叠代器中還有元素則返回true 7     */ 8     boolean hasNext(); 9 10     /**11     *返回叠代器中的下一個元素12     */13     E next();14 15     /**16     *通過叠代器刪除處於集合中最底層的元素17     */18     void remove();19 }

技術分享

Iterator是一個接口,那如何來創建一個實例呢?要記住,叠代器和集合類的關系非常緊密,我們可以通過集合類來創建一個Iterator實例,ArrayList、LinkedList、Vector都有對它的實現。我們來看ArrayList是如何創建一個Iterator叠代器實例的。在此之前我們先來看看集合和叠代器之間的繼承關系。

技術分享

由於集合的關系相對來說比較復雜,在此我們主要看註釋部分,通過閱讀源代碼會發現ArrayList覆寫了AbstractList抽象類中的iterator方法並聲稱效果更佳,而LinkedList則沒有覆寫,由此可判斷ArrayList的iterator方法比LinkedList中的iterator方法更為高效。

我們直接看ArrayList裏中實現的iterator方法。

1 public Iterator<E> iterator() {2     return new Itr();3 }

從代碼來看它返回類一個Itr的對象實例,順著代碼看看這個Itr類是什麽。

技術分享

 1 private class Itr implements Iterator<E> { 2     int cursor;       // 返回下一個元素的索引 3     int lastRet = -1; // 返回最後一個元素的索引;如果沒有則返回-1 4     int expectedModCount = modCount; 5  6     public boolean hasNext() { 7         return cursor != size; 8     } 9 10     @SuppressWarnings("unchecked")11     public E next() {12         checkForComodification();13         int i = cursor;14         if (i >= size)15             throw new NoSuchElementException();16         Object[] elementData = ArrayList.this.elementData;17         if (i >= elementData.length)18             throw new ConcurrentModificationException();19         cursor = i + 1;20         return (E) elementData[lastRet = i];21     }22 23     public void remove() {24         if (lastRet < 0)25             throw new IllegalStateException();26         checkForComodification();27 28         try {29             ArrayList.this.remove(lastRet);30             cursor = lastRet;31             lastRet = -1;32             expectedModCount = modCount;33         } catch (IndexOutOfBoundsException ex) {34             throw new ConcurrentModificationException();35         }36     }37 38     final void checkForComodification() {39         if (modCount != expectedModCount)40             throw new ConcurrentModificationException();41     }42 }

技術分享

原來Itr它是一個私有的內部類,實現Iterator接口。

我們來一行一行讀。在第3行中有一個modCount變量。跟蹤這個變量,發現這個變量有點意思:

protected transient int modCount = 0;

發現有一個“transient”關鍵字,查閱資料發現這個關鍵字的意思是:表示一個域不是該對象序列化的一部分。意思是在對象被序列化時不包括這個變量,至於為什麽要這麽做呢,我們可以留下一個疑問。(JDk源碼註釋中是這麽說的:The modCount value that the iterator believes that the backing List should have. If this expectation is violated, the iterator has detected concurrent modification.英語太次只能讀懂最後一句:如果這個期望是可見性的,那麽這個叠代器會檢測到有一個並發的修改。猜測是和並發多線程相關。)

hasnext的實現較為簡單:

1 public boolean hasNext() {2     return cursor != size;  //下一個元素的索引是否等於ArrayList的大小3 }

next的實現:

技術分享

public E next() {
    checkForComodification();  //檢查是否並發修改    int i = cursor;    if (i >= size)        throw new NoSuchElementException();  //索引大於ArrayList大小拋出異常
    Object[] elementData = ArrayList.this.elementData;  //後面實際是在取ArrayList中的數據    if (i >= elementData.length)        throw new ConcurrentModificationException();
    cursor = i + 1;    return (E) elementData[lastRet = i];
}

技術分享

在next方法中我們看到有一個checkForCommodification方法:

final void checkForComodification() {    if (modCount != expectedModCount)        throw new ConcurrentModificationException();
}

看來這個modCount變量確實是和並發相關,如果expectedModCount和modCount這兩個值不同,則拋出當前正在並發修改的異常。

最後我們來看remove方法的實現:

技術分享

public void remove() {    if (lastRet < 0)  //這個地方格外註意,不能在未調用next時直接調用remove方法,必須在remove調用前調用next方法,將通過cursor索引值將其值賦給lastRet        throw new IllegalStateException();
    checkForComodification();    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {        throw new ConcurrentModificationException();
    }
}

技術分享

在remove方法中我們要格外註意,在第一句是檢測lastRet是否小於0,我們初始化了lastRet變量-1的值,這意味著,如果我們如果創建完Iterator實例後直接調用remove方法會拋出一個IllegalStateException異常,那怎麽才能正確調用呢?那就是在調用remove方法前先調用next方法,此時lastReturn通過cursor索引被賦值,這個時候才能正確使用remove方法。同時它也會調用checkForCommodification方法做並發修改檢測。其實我們可以看到JDK源碼之所以寫到好,是因為它每個方法都做了很多的檢測,以確保在盡量多的場景下準確無誤地運行。今天關於Java的叠代器就通過JDK源碼簡單介紹,通過對源碼的閱讀能夠加深我們的理解,這還只是簡單的閱讀,並沒有做很深的理解。最後,我們以為一個Iterator的例子結尾。

技術分享

 1 package day_29_iterator; 2  3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6  7 /** 8  * @author turbo 9  *10  * 2016年9月29日11  */12 public class Main {13 14     /**15      * @param args16      */17     public static void main(String[] args) {18         List list = new ArrayList();19         list.add(1);20         list.add(2);21         Iterator iterator = list.iterator();22         while (iterator.hasNext()){23             System.out.println(iterator.next());24         }25     }26 27 }

技術分享


Java叠代器Iterator