Java學習73:使用Iterator

創建包com.tenth.iterator,在包下創建Demo13.java

Java的集合類都可以使用for…each循環,List、Set和Queue會迭代每個元素,Map會迭代每個Key。以List爲例:

List<String> list = List.of("Apple", "Orange", "Pear");
for (String s : list) {
    System.out.println(s);
}

實際上,Java編譯器並不知道如何遍歷List。上述代碼能夠編譯通過,只是因爲編譯器把for each循環通過Iterator改寫爲了普通的for循環:

for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
     String s = it.next();
     System.out.println(s);
}

我們把這種通過Iterator對象遍歷集合的模式稱爲迭代器。

使用迭代器的好處在於,調用方總是以統一的方式遍歷各種集合類型,而不必關係它們內部的存儲結構。

例如,我們雖然知道ArrayList在內部是以數組形式存儲元素,並且,它還提供了get(int)方法。雖然我們可以用for循環遍歷:

for (int i=0; i<list.size(); i++) {
    Object value = list.get(i);
}

但是這樣一來,調用方就必須知道集合的內部存儲結構。並且,如果把ArrayList換成LinkedList,get(int)方法耗時會隨着index的增加而增加。如果把ArrayList換成Set,上述代碼就無法編譯,因爲Set內部沒有索引。

用Iterator遍歷就沒有上述問題,因爲Iterator對象是集合對象自己在內部創建的,它自己知道如何高效遍歷內部的數據集合,調用方則獲得了統一的代碼,編譯器才能把標準的for each循環自動轉換爲Iterator遍歷。

如果我們自己編寫了一個集合類,想要使用for each循環,只需滿足以下條件:

  • 集合類實現Iterable接口,該接口要求返回一個Iterator對象;
  • 用Iterator對象迭代集合內部數據。

這裏的關鍵在於,集合類通過調用iterator()方法,返回一個Iterator對象,這個對象必須自己知道如何遍歷該集合。

一個簡單的Iterator示例如下,它總是以倒序遍歷集合:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo13 {
    public static void main(String[] args) {
        ReverseList<String> rlist = new ReverseList<>();
        rlist.add("Apple");
        rlist.add("Orange");
        rlist.add("Pear");
        for (String s : rlist) {
            System.out.println(s);
        }
    }
}

class ReverseList<T> implements Iterable<T> {

    private List<T> list = new ArrayList<>();

    public void add(T t) {
        list.add(t);
    }

    @Override
    public Iterator<T> iterator() {
        return new ReverseIterator(list.size());
    }

    class ReverseIterator implements Iterator<T> {
        int index;

        ReverseIterator(int index) {
            this.index = index;
        }

        @Override
        public boolean hasNext() {
            return index > 0;
        }

        @Override
        public T next() {
            index--;
            return ReverseList.this.list.get(index);
        }
    }
}

雖然ReverseList和ReverseIterator的實現類稍微比較複雜,但是,注意到這是底層集合庫,只需編寫一次。而調用方則完全按for each循環編寫代碼,根本不需要知道集合內部的存儲邏輯和遍歷邏輯。

在編寫Iterator的時候,我們通常可以用一個內部類來實現Iterator接口,這個內部類可以直接訪問對應的外部類的所有字段和方法。例如,上述代碼中,內部類ReverseIterator可以用ReverseList.this獲得當前外部類的this引用,然後,通過這個this引用就可以訪問ReverseList的所有字段和方法。

小結
Iterator是一種抽象的數據訪問模型。使用Iterator模式進行迭代的好處有:

  • 對任何集合都採用同一種訪問模型;
  • 調用者對集合內部結構一無所知;
  • 集合類返回的Iterator對象知道如何迭代。

Java提供了標準的迭代器模型,即集合類實現java.util.Iterable接口,返回java.util.Iterator實例。

謝謝觀看

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