定义与结构
迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
从定义可见,迭代器模式是为容器而生。很明显,对容器对象的访问必然涉及到遍历算法。你可以一股脑的将遍历方法塞到容器对象中去;或者根本不去提供什么遍历算法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。
然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。
而迭代器模式的出现,很好的解决了上面两种情况的弊端。先来看下迭代器模式的真面目吧。
迭代器模式由以下角色组成:
1) 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
2) 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
3) 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
4) 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。
简单来说,迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
迭代器实例:
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorTest
{
public static void main(String[]args)
{
Collection books=new HashSet();
books.add("Java编程思想");
books.add("十万个为什么");
books.add("疯狂讲义");
Iterator it=books.iterator();
while(it.hasNext())
{
String book=(String)it.next();
System.out.println(book);
if(book.equals("十万个为什么"))
{
it.remove();
}
book="hello world";
}
System.out.println(books);
}
}
运行结果:
在很多书上对iterator有一个误区,“当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何影响。”
这句话其实是不成立的。从上面的例子运行结果来看,这句话好像是成立的,这只是因为String是Java中一个很特殊的类,它的特殊之外就在于它是一个不可变类,即String对象一旦生成便不可改变,经常看到的对String变量的赋值其实是让引用指向新的String对象!
也就是说,在本文的例子中,语句String book=(String)it.next();的作用只是让book也指向it.next()所指向的对象,后面book=”hello world”;只是让book这个引用指向一个新的字符串对象,显然这种改变不会也不可能影响原对象,it.next()所指向的另然是原来的那个对象,所以从结果来看好像结论挺有道理。但是实际上Iterator.next()确实是提供了原对象的引用,对于除不可变类之外的其他对象,都可以通过这个引用来改变它。
所以修改迭代变量的值对集合元素本身也会有影响。
使用迭代器,可以封装容器的内部实现细节,对于不同的集合,可以提供同一的遍历方式,简化客户端的访问和获取容器内数据。