java 中 Iterator 和 Iterable 區別

java 中 Iterator 和 Iterable 區別

Iterator(迭代器)

作爲一種設計模式,迭代器可以用於遍歷一個對象,而開發人員不用去了解這個對象的底層結構。這裏就不仔細說迭代器這種設計模式,因爲我們主要的目的是探索java中Iterator和 Iterable之間的區別

用法

首先來說一下他們各自是怎麼使用,不會用談什麼都是瞎搞。

Iterator用法

首先來說一下Iterator這個接口,他定義了迭代器基本的功能。

源碼如下

package java.util;

public interface Iterator<E> {
    boolean hasNext();   //返回是否有下一個元素
    E next();            //返回下一個元素
    void remove();       //移除當前元素
}

如何使用這個接口

public class main {

    public static void main(String[] args) {
        MyIterator mi = new MyIterator();
        while (mi.hasNext()){
            System.out.printf("%s\t",mi.next());
        }
    }
}

class MyIterator implements Iterator<String>{
    private String[] words = ("And that is how "
            + "we know the Earth to be banana-shaped.").split(" ");
    private int count = 0;
    public MyIterator() {
    }

    @Override
    public boolean hasNext() {
        return count < words.length;
    }

    @Override
    public String next() {
        return words[count++];
    }
}

代碼很簡單,所以就不過多的解釋了,但是有一點需要注意的是,下面這樣使用上面定義的MyIterator類是錯誤的,你可以試一下。

for (String s:new MyIterator){
            System.out.printf("%s\t",s);
 }

Iterable用法

源碼

package java.lang;

import java.util.Iterator;

public interface Iterable<T> {

    Iterator<T> iterator();   
}

如何使用

public class main {

    public static void main(String[] args) {

      MyIterable myIterable= new MyIterable();
        Iterator<String> mIterator = myIterable.iterator();
        for (String s:myIterable){
            System.out.printf("%s\t",s);
        }
    }
}

class MyIterable implements Iterable<String>{
    private String[] words = ("And that is how "
            + "we know the Earth to be banana-shaped.").split(" ");

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int i = 0;
            @Override
            public boolean hasNext() {
                return i < words.length;
            }

            @Override
            public String next() {
                return words[i++];
            }
        };
    }
}

與上面Iterator不同的是,這個類還可以下面這樣使用

        MyIterable mi = new MyIterable().iterator();
        while (mi.hasNext()){
            System.out.printf("%s\t",mi.next());
        }

區別

基本用法已經說完,相信你也能看出其中的一些區別

  1. Iterator是迭代器類(這個類是指定義了迭代器基本需要的方法),而Iterable是接口。 這個從他們的包名就可以看出來。
java.lang.Iterable 
java.util.Iterator 
  1. Iterator不嫩用於foreach循環語句,Iterable可以

  2. 爲什麼一定要實現Iterable接口,爲什麼不直接實現Iterator接口呢?

    看一下JDK中的集合類,比如List一族或者Set一族,都是實現了Iterable接口,但並不直接實現Iterator接口。這並不是沒有道理的。因爲Iterator接口的核心方法next()或者hasNext() 是依賴於迭代器的當前迭代位置的。如果Collection直接實現Iterator接口,勢必導致集合對象中包含當前迭代位置的數據(指針)。當集合在不同方法間被傳遞時,由於當前迭代位置不可預置,那麼next()方法的結果會變成不可預知。 除非再爲Iterator接口添加一個reset()方法,用來重置當前迭代位置。但即時這樣,Collection也只能同時存在一個當前迭代位置。而Iterable則不然,每次調用都會返回一個從頭開始計數的迭代器。多個迭代器是互不干擾的。

擴展

你在看ArrayList源碼的時候,你會發現這樣一段代碼

   private class Itr implements Iterator<E> {
       int cursor;       // 返回下一個元素的索引

      int lastRet = -1; // 返回最後一個元素的索引,如果空,返回-1

       int expectedModCount = modCount; //用於檢測當前集合是否執行了添加刪除操作,其中modCount,是當前集合中元素的個數

       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) {
           Objects.requireNonNull(consumer);
           final int size = ArrayList.this.size;
           int i = cursor;
           if (i >= size) {
               return;
           }
           final Object[] elementData = ArrayList.this.elementData;
           if (i >= elementData.length) {
               throw new ConcurrentModificationException();
           }
           while (i != size && modCount == expectedModCount) {
               consumer.accept((E) elementData[i++]);
           }
           // update once at end of iteration to reduce heap write traffic
           cursor = i;
           lastRet = i - 1;
           checkForComodification();
       }
      //如果發生添加刪除操作,則拋出錯誤。
       final void checkForComodification() {
           if (modCount != expectedModCount)
               throw new ConcurrentModificationException();
       }
   }

​ 對於上述的代碼不難看懂,有點疑惑的是int expectedModCount = modCount;這句代碼

​ 其實這是集合迭代中的一種“快速失敗”機制,這種機制提供迭代過程中集合的安全性。閱讀源碼

​ 就可以知道ArrayList中存在modCount對象,增刪操作都會使modCount++,通過兩者的對比

​ 迭代器可以快速的知道迭代過程中是否存在list.add()類似的操作,存在的話快速失敗!

Fail-Fast(快速失敗)機制

仔細觀察上述的各個方法,我們在源碼中就會發現一個特別的屬性modCount,API解釋如下:

​ The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress
may yield incorrect results.

記錄修改此列表的次數:包括改變列表的結構,改變列表的大小,打亂列表的順序等使正在進行 迭代產生 錯誤的結果。

Tips:僅僅設置元素的值並不是結構的修改

我們知道的是ArrayList是線程不安全的,如果在使用迭代器的過程中有其他的線程修改了List就會

拋出ConcurrentModificationException這就是Fail-Fast機制。

那麼快速失敗究竟是個什麼意思呢?

在ArrayList類創建迭代器之後,除非通過迭代器自身remove或add對列表結構進行修改,否則在其他

​ 線程中以任何形式對列表進行修改,迭代器馬上會拋出異常,快速失敗。

迭代器的好處

迭代器的好處

​ 通過上述我們明白了迭代是到底是個什麼,迭代器的使用也十分的簡單。現在簡要的總結下使用迭代器的好處吧。

  1. 迭代器可以提供統一的迭代方式。**
  2. 迭代器也可以在對客戶端透明的情況下,提供各種不同的迭代方式。

不過對於第三點尚需注意的是:就像上述事例代碼一樣,我們不能保證迭代過程中出現“快速失敗”的都是因爲同步造成的,因此爲了保證迭代操作的正確性而去依賴此類異常是錯誤的!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章