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接口

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