Java - Iterable接口、迭代器Iterator

  • 所有的集合類(List、Set...)都實現自Collection接口,而Collection接口又繼承於Iterable接口,因此可以說所有的集合類(List、Set...)都實現了Iterable接口

  • 當某個類實現Iterable接口時,我們就能稱這個類是一個 "可數" 的類,也就是可以使用iterator()獲取一個迭代器Iterator,然後使用這個Iterator實例去遍歷這個類,因此所有的Collection類都能夠使用迭代器Iterator來遍歷

    • Iterable接口

      public interface Iterable<T> {
          //當某個類實現Iterable接口的話,就能獲取到迭代器iterator
          //然後就能使用這個iterator去遍歷此類
          Iterator<T> iterator();
      }
    • Iterator接口

      • 如果某個類實現了Iterable接口,那麼他也需要創建一個內部類去實現一個Iterator類,讓調用Iterable接口中的iterator()時,能夠獲取到一個iterator實例

        public interface Iterator<E> {
            //是否有下一個元素
            boolean hasNext();
        ​
            //取得下一個元素
            E next();
            
            //刪除最後一個獲取的元素,因此調用remove()前一定得先調用一次next()
            void remove();    
        }
      • 至於此Iterator接口怎麼實現,就看各個集合實現類如何定義 "下一個元素",像是ArrayList的下一個元素就是element[index+1],而HashMap的下一個元素則是hash table數組中儲存的下一個entry

      • 另外可以想像Iterator像是一個遊標一樣,一開始停在最前面,然後不停的往後走(只能向後移動),且此遊標每次都是停在元素和元素的中間,當調用next時,迭代器就越過下一個元素,並返回剛剛越過的那個元素的引用

    • 使用迭代器Iterator遍歷ArrayList

      public class Main {
          public static void main(String[] args) {
              //ArrayList實現了Collection接口,因此他也實現了Iterable接口
              //所以他可以使用iterator迭代器來遍歷
              List<String> list = new ArrayList<>();
              list.add("hello");
              list.add("world");
             
              //調用Iterable接口中的iterator()取得此ArrayList的迭代器實例
              Iterator<String> its = list.iterator();
              
              //使用Iterator接口的hasNext()、next()來遍歷此ArrayList集合實現類
              while (true) {
                  if (its.hasNext()) {
                      String s = its.next();
                      System.out.println(s);
                  } else {
                      break;
                  }
              }
          }
      }
  • 而再進一步說,當某個類能使用迭代器Iterator來遍歷時,就能使用java提供的foreach語法糖來遍歷此類 (foreach語法糖其實就是簡化的iterator())

    • foreach實際上會被編譯器編譯成使用迭代器iterator()去遍歷集合,因此能使用foreach的,都是得實現Iterable接口的集合類Collection們,像是List、Set

    • 所以Map就沒有辦法直接使用foreach(因爲Map沒有實現Iterable接口),只有他的map.entrySet()map.keySet()map.values()這種返回一個集合類的方法,才能使用foreach

    public class Main {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("hello");
            list.add("world");
    ​
            //原代碼,使用語法糖的foreach
            for (String s : list) {
                System.out.println(s);
            }
    ​
            //實際上會被編譯成使用iterator去遍歷
            for (Iterator<String> its = list.iterator(); its.hasNext(); ) {
                String s = its.next();
                System.out.println(s);
            }
        }
    }
  • 爲什麼Iterator要額外使用內部類去實現,而不是ArrayList直接實現此接口 ?

    • 如果看過Collection類的源碼(以ArrayList爲例),可以發現ArrayList類並不是由ArrayList去實現Iterator接口,而是ArrayList有一個內部類 Itr,專門去實現Iterator接口,而ArrayList的iterator()方法,只是去創建一個內部類ArrayList.Itr的實例而已

      //ArrayList不實現Iterator接口,反而是由他的內部類進行實現
      public class ArrayList<E> extends AbstractList<E> {
          //調用list.iterator()可以取得此list的迭代器
          public Iterator<E> iterator() {
              return new Itr(); //實際上就是去創建一個內部類的實例
          }
      ​
          //ArrayList中的內部類Itr,專門實現Iterator接口
          private class Itr implements Iterator<E> {
              int cursor; //記錄當前迭代到哪裡
      ​
              public boolean hasNext() { ... }
              public E next() { ... }
              public void remove() { ... }
          }
      }
    • 要這樣設計是因爲一個集合類可能同時有多個迭代器去遍歷他,而每個迭代器遍歷到集合的哪裡,是每個迭代器自己的事情,彼此不互相干涉,因此才需要額外使用一個內部類去實現迭代器的Iterator接口

      • 如此當需要用到Iterator來遍歷集合時,只需要調用list.iterator(),就能取得一個全新的、不受別人影響的迭代器供自己使用,而迭代器彼此之間也不會互相干涉

      • 至於爲什麼要特別使用內部類來實現Iterator接口,而不是創建一個Iterator公共類來供所有集合一起使用,是因爲迭代器需要知道集合的內部結構,他才能知道要怎麼去實現hasNext()next()remove()方法,而使用內部類才能無條件的取用外部類的所有信息(包含private的變量和方法),因此才需要將Iterator提取成接口,讓每個集合自己使用內部類去實現Iterator接口

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