Java的集合类都可以使用for each循环,List、Set 和Queue会迭代每个元素,Map会迭代每个Key。以List为例
List<String> list = List.of("apple","Orange","Banana");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 index)方法。
我们可以用for循环遍历
for(int i = 0; i<list.size();i++){Object value = list.get(i);}
但是这样一来,调用方就必须知道集合的内部存储结构。
并且,如果把ArrayList换成LinkedList,get(int index)方法耗时会随着index的增加而增加。
如果把ArrayList换成Set,上述代码就无法编译,因为Set内部没有索引。
如果我们自己编写了一个集合类,想要使用for each循环,只需要满足以下条件:
- 集合类实现
Iterable接口,该接口要求返回一个Iterator对象 - 用
Iterator对象迭代集合内部数据
这里的关键,集合类通过调用iterator()方法,返回一个Iterator对象,这个对象必须自己知道如何遍历该集合。
public class Main {public static void main(String[] args){ReverseList<String> rlist = new ReverseList<>();rlist.add("apple");rlist.add("orange");rlist.add("banana");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);}public Iterator<T> iterator(){return new ReverseIterator(list.size());}class ReverseIterator implements Iterator<T> {int index;ReverseIterator(int index){this.index = index;}@overridepublic boolean hasNext(){return index > 0;}@Overridepublic T next(){index--;return ReverseList.this.list.get(index);}}}
在编写Iterator的时候,我们通常可以用一个内部类来实现Iterator接口,这个内部类可以直接访问对应的外部类的所有字段和方法。
小结
Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有
- 对任何集合都采用同一种访问模型
- 调用者对集合内部结构一无所知
- 集合类返回的
Iterator对象知道了如何迭代。
Java提供了标准的迭代器模型。即集合类实现java.util.Iterable接口,返回java.util.Iterator实例
