迭代器模式(Iterator Pattern)
迭代器模式是非常常见的设计模式,属于行为型模式,迭代器模式提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
迭代器模式的优缺点
优点:
- 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
- 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
- 提供了一种 设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把 管理对象集合和 遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
缺点:
-
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类。
-
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。
类图:
(这里只画出基于集合实现的迭代器类图,内部类的形式在类图中用组合关系来描述)
代码实现:
//迭代器接口
public interface Iterator {
boolean hasNext();//判断是否还有元素
Object next();//取出元素
}
//具体的迭代器:基于数组
public class ArrayIterator implements Iterator{
private String[] s;//代表容器存放元素的方式是数组
private int index;//数组的下标
public ArrayIterator(String[] s) {
this.s=s;
}
@Override
public boolean hasNext() {
if(index>=s.length||s[index]==null){
return false;
}
return true;
}
@Override
public Object next() {
return s[index++];
}
}
//容器接口:定义了所有容器的统一方法——获取迭代器
public interface Container {
public Iterator getIterator();
}
//具体的容器:容器存放元素的方式为数组
public class ArrayContainer implements Container{
String[] s={"aa","bb","cc"};
@Override
public Iterator getIterator() {
return new ArrayIterator(s);
}
}
//具体的容器:容器存放元素的方式为集合
public class ListContainer implements Container{
public List<String> list=new ArrayList<String>();
@Override
public Iterator getIterator() {
return new ListIterator();
}
//内部类的形式:内部类可以直接使用外部类的元素,也就是可以直接使用到容器存放元素的集合进行遍历
//具体的迭代器:基于集合
class ListIterator implements Iterator{
private int index;//集合的索引
@Override
public boolean hasNext() {
if(index>=list.size()){
return false;
}
return true;
}
@Override
public Object next() {
return list.get(index++);
}
}
}
public class Client {
public static void main(String[] args) {
Container c=new ArrayContainer();
Iterator iterator=c.getIterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("===========================");
ListContainer c2=new ListContainer();
c2.list.add("11");
c2.list.add("22");
c2.list.add("33");
Iterator iterator2=c2.getIterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
在以上代码中,我们自定义了两个迭代器,一个是基于数组实现,一个是基于集合实现,也就是说容器存放元素的两种方式一般是数组或集合,使用迭代器模式之后,客户端要遍历容器中的元素非常方便,只需要获取到对应的迭代器即可,并不需要知道对应容器的内部细节,客户端不需要关心容器底层是通过什么方式实现的,当然了,对应的迭代器就需要知道他要迭代的容器底层是基于什么方式实现的,这样才能正确的迭代容器中的元素。
另外,上面的代码中,基于集合实现的迭代器使用了内部类的形式,将具体的迭代器以内部类的形式组合到具体的容器类中来,这种方式更为实用,我们查看jdk关于迭代器的源码也可以知道,迭代器的实现都是以内部类的形式。
迭代器模式在JDK源码中的应用
public class Main {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
通过上面的代码,我们可以看到,List集合中有一个返回迭代器的方法,通过该迭代器可以遍历集合中的元素;关于迭代器的实现,JDK源码中已经帮我们实现好了,通过追踪源码可以知道,List接口就相当于容器接口,该接口中提供了操作集合的一些方法以及获取迭代器的方法;ArrayList类实现了List接口,实现了返回迭代器的方法;Iterator接口(位于java.util包下)也就是迭代器接口,提供了hasNext()、next()、remove()三个主要的方法;Itr类是Iterator接口的一个实现类,也就是具体的迭代器,以内部类的形式存在于ArrayList类中,另外,ArrayList集合存放元素是以数组形式存放的!
关于JDK源码中提供的Iterator迭代器,在《java疯狂讲义》一书中是这样描述的:
需要注意的是:
- 对于迭代器接口Iterator提供的remove()方法,在使用之前,必须是已经执行过一次next()方法了,并且只能是一次,否则的话,会报java.util.IllegalStateException异常;
- 当使用Iterator迭代访问Collection集合时,Collection集合中的元素不能被改变(也就是说不能调用Collection接口中的方法改变集合元素),只有通过Iterator的remove()方法删除上一次next()方法返回的集合元素才可以,否则将会引发java.util.ConcurrentModificationException异常。
例子:
public class Main {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
iterator.remove();//调用Iterator的remove()方法删除上一次next()方法返回的集合元素
}
System.out.println(list.size());
}
}
执行结果:
public class Main {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String s = iterator.next();
list.remove(s);//调用Collection接口中的remove方法改变集合
}
System.out.println(list.size());
}
}
执行结果: