天天看點

Java - Iterable接口、疊代器Iterator

  • 所有的集合類(List、Set...)都實作自Collection接口,而Collection接口又繼承于Iterable接口,是以可以說所有的集合類(List、Set...)都實作了Iterable接口
    • Java - Iterable接口、疊代器Iterator
  • 當某個類實作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時,疊代器就越過下一個元素,并傳回剛剛越過的那個元素的引用
      • Java - Iterable接口、疊代器Iterator
    • 使用疊代器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接口