Java的集合类都可以使用for each循环,ListSetQueue会迭代每个元素,Map会迭代每个Key。以List为例

  1. List<String> list = List.of("apple","Orange","Banana");
  2. for(String s : list){
  3. System.out.println(s);
  4. }

实际上,Java编译器并不知道如何遍历List,上述代码能够编译通过,只是因为编译器把for each循环通过Iterator改写为了普通的for循环。

  1. for (Iterator<String> it = List.iterator();it.hasNext();){
  2. String s = it.next();
  3. System.out.println(s);
  4. }

我们把这种通过Iterator对象遍历集合的模式称为迭代器。
使用迭代器的好处在于,调用方问题以统一的方式遍历各种集合类型,而不必关心它们内部的存储结构。
例如,我们虽然知道ArrayList在内部是以数组形式存储元素,并且,它还提供了get(int index)方法。
我们可以用for循环遍历

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

但是这样一来,调用方就必须知道集合的内部存储结构。
并且,如果把ArrayList换成LinkedListget(int index)方法耗时会随着index的增加而增加。
如果把ArrayList换成Set,上述代码就无法编译,因为Set内部没有索引。

如果我们自己编写了一个集合类,想要使用for each循环,只需要满足以下条件:

  • 集合类实现Iterable接口,该接口要求返回一个Iterator对象
  • Iterator对象迭代集合内部数据

这里的关键,集合类通过调用iterator()方法,返回一个Iterator对象,这个对象必须自己知道如何遍历该集合。

  1. public class Main {
  2. public static void main(String[] args){
  3. ReverseList<String> rlist = new ReverseList<>();
  4. rlist.add("apple");
  5. rlist.add("orange");
  6. rlist.add("banana");
  7. rlist.add("pear");
  8. for(String s : rlist){
  9. System.out.println(s);
  10. }
  11. }
  12. }
  13. class ReverseList<T> implements Iterable<T> {
  14. private List<T> list = new ArrayList<>();
  15. public void add(T t){list.add(t);}
  16. public Iterator<T> iterator(){
  17. return new ReverseIterator(list.size());
  18. }
  19. class ReverseIterator implements Iterator<T> {
  20. int index;
  21. ReverseIterator(int index){this.index = index;}
  22. @override
  23. public boolean hasNext(){return index > 0;}
  24. @Override
  25. public T next(){
  26. index--;
  27. return ReverseList.this.list.get(index);
  28. }
  29. }
  30. }

在编写Iterator的时候,我们通常可以用一个内部类来实现Iterator接口,这个内部类可以直接访问对应的外部类的所有字段和方法。

小结

Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有

  • 对任何集合都采用同一种访问模型
  • 调用者对集合内部结构一无所知
  • 集合类返回的Iterator对象知道了如何迭代。

Java提供了标准的迭代器模型。即集合类实现java.util.Iterable接口,返回java.util.Iterator实例