由于Java中数据容器众多,而对数据容器的操作在很多时候都具有极大的共性,于是Java采用了迭代器为各种容器提供公共的操作接口。使用Java的迭代器iterator可以使得对容器的遍历操作完全与其底层相隔离,可以到达极好的解耦效果。
迭代器的目的就是不用关心容器类型,而可以遍历序列中的对象。 客户端程序员不必知道或者关心该序列底层的结构。此外迭代器被称为轻量级对象,因为创建它的代价小。
public interface Iterable<T>
Iterator<T> iterator()
Returns an iterator over a set of elements of type T
Collection接口拓展了接口Iterable,根据以上的对Iterable接口的定义可以发现,其要求实现其的类都提供一个返回迭代器Iterator<T>对象的方法。
迭代器Iterator<T>接口的的定义为:
Interface Iterator<E>
boolean hasNext()
Returns true if the iteration has more elements.
E next()
Returns the next element in the iteration.
void remove()
Removes from the underlying collection the last element returned by the iterator (optional operation).
注意:
从以上的定义中可以发现,似乎Iterable()接口和Iterator()接口完全一致,没有任何区别。结合刚刚学习的内部类,可以发现这又是一个支持程序多样化的巧妙设计,充分的支持了多态和解耦。
迭代器是遍历Collection的工具,无论什么类型的Collection都可以用iterator来遍历。
例:
public class Test {
public static void main(String[] args) {
ArrayList<String> l = new ArrayList<String>();
l.add("a");
l.add("b");
l.add("c");
l.add("d");
l.add("e");
l.add("f");
l.add("g");
l.add("h");
System.out.println(l);
ListIterator<String> listIterator = l.listIterator();
String s1 = listIterator.next();
System.out.println("s1: " + s1);
String s2 = listIterator.next();
System.out.println("s2: " + s2);
String s1Previous = listIterator.previous();
System.out.println("s1Pre: " + s1Previous);
String sPrevious2 = listIterator.previous();
System.out.println("sPre2: " + sPrevious2);
}
}
返回结果
[a, b, c, d, e, f, g, h]
s1: a
s2: b
s1Pre: b
sPre2: a
iterator的工作示意图:
iterator是这样工作的,首先声明的是iterator的cursor指向的是元素之间的间隔位置(如上图所示 Iterator1和Iterator的位置)
当调用iterator.next()时,官方文档是这样写的:
Returns the next element in the list and advances the cursor position.
意思是,返回当前指针的下一个元素,并且推进指针到下一个位置。
举个例子:
list.iterator() // cursor处在图中Iterator1的位置
iterator.next() // 返回list的元素1也就是图中的List1, 然后cursor推进一个位置到Iterator2的位置,如此往复进行遍历
客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。 客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合 。
java.util.Iterator接口的定义:
public interface Iterator { boolean hasNext(); Object next(); void remove(); }
hasNext():遍历过程中,判定是否还有下一个元素。(从Collection对象的第一个元素开始)
next(): 遍历该元素。(即取出下一个元素)
remove(): 移除刚刚遍历过的元素。
在遍历下一个元素前,先判断其是否存在。对于想删除的元素,必须先遍历其,故 remove()方法总是接在 next()方法之后。
依赖前两个方法就能完成遍历,典型的代码如下:
for(Iterator it = c.iterator(); it.hasNext(); ) { Object o = it.next(); // 对o的操作... }
在JDK1.5中,还对上面的代码在语法上作了简化(但是限于只读,如果需要remove,还是直接使用iterator):
// Type是具体的类型,如String。 for(Type t : c) { // 对t的操作... }
每一种集合类返回的Iterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回SetIterator,Tree可能返回TreeIterator,但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种Iterator,它只需要获得这个Iterator接口即可
为什么一定要去实现Iterable这个接口呢? 为什么不直接实现Iterator接口呢?
看一下JDK中的集合类,比如List一族或者Set一族,
都是实现了Iterable接口,但并不直接实现Iterator接口。
仔细想一下这么做是有道理的。因为Iterator接口的核心方法next()或者hasNext()
是依赖于迭代器的当前迭代位置的。
如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即时这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。
多个迭代器是互不干扰的。