一、什么是迭代器模式
实际开发中,我们针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但我们不希望在集合容器的抽象接口层中充斥着各种不同的遍历操作,这时候我们就需要一种能完成下面功能的迭代器:
- (1)遍历一个集合对象
- (2)不需要了解聚合对象的内部结构
- (3)提供多种不同的遍历方式
迭代器模式提供一种访问集合中的各个元素,而不暴露其内部表示的方法。将在元素之间游走的职责交给迭代器,而不是集合对象,从而简化集合容器的实现,让集合容器专注于在它所应该专注的事情上,更加符合单一职责原则。
二、UML结构图
- Iterator:抽象迭代器,提供了在集合容器元素之间游走的方法。
- ConcreteIterator:具体迭代器,能够对具体的集合容器进行遍历,每种集合容器都应该对应一个具体的迭代器
- Aggregate:抽象容器类
- ConcreteAggregate:具体容器类,实现 creatorIterator() 方法,返回该聚合对象的迭代器。
三、代码实现
3.1、UML结果图
3.2、案例1
这里自己手写一个java简易的迭代器
Collection_接口
package com.company.Iterator.v5;
public interface Collection_ {
void add(Object o);
int size();
Iterator_ iterator();
}
Iterator接口
package com.company.Iterator.v5;
public interface Iterator_ {
boolean hasNext();
Object next();
}
ArrayList_类
package com.company.Iterator.v5;
/**
* 相比数组,这个容器不用考虑边界问题,可以动态扩展
*/
class ArrayList_ implements Collection_ {
Object[] objects = new Object[10];
//objects中下一个空的位置在哪儿,或者说,目前容器中有多少个元素
private int index = 0;
public void add(Object o) {
if(index == objects.length) {
Object[] newObjects = new Object[objects.length*2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects = newObjects;
}
objects[index] = o;
index ++;
}
public int size() {
return index;
}
@Override
public Iterator_ iterator() {
return new ArrayListIterator();
}
private class ArrayListIterator implements Iterator_{
private int currentIndex = 0;
@Override
public boolean hasNext() {
if(currentIndex >= index) return false;
return true;
}
@Override
public Object next() {
Object o = objects[currentIndex];
currentIndex ++;
return o;
}
}
}
主函数
package com.company.Iterator.v5;
/**
* v1:构建一个容器,可以添加对象
* v2:用链表来实现一个容器
* v3:添加容器的共同接口,实现容器的替换
* v4:如何对容器遍历呢?
* v4:用一种统一的遍历方式,要求每一个容器都要提供Iterator的实现类
* 作业:实现LinkedList的Iterator
*/
public class Main {
public static void main(String[] args) {
Collection_ list = new ArrayList_();
for(int i=0; i<15; i++) {
list.add(new String("s" + i));
}
System.out.println(list.size());
//这个接口的调用方式:
Iterator_ it = list.iterator();
while(it.hasNext()) {
Object o = it.next();
System.out.println(o);
}
}
}
运行结果
3.3、案例2
把泛型加上(改进版)
Collection_接口
package com.company.Iterator.v7;
public interface Collection_<E> {
void add(E o);
int size();
Iterator_ iterator();
}
Iterator接口
package com.company.Iterator.v7;
public interface Iterator_<E> { //Element //Type //K //Value V Tank
boolean hasNext();
E next(); //Tank next() Iterator_<Tank> it = ... Tank t = it.next();
}
ArrayList_类
package com.company.Iterator.v7;
/**
* 相比数组,这个容器不用考虑边界问题,可以动态扩展
*/
class ArrayList_<E> implements Collection_<E> {
E[] objects = (E[])new Object[10];
//objects中下一个空的位置在哪儿,或者说,目前容器中有多少个元素
private int index = 0;
public void add(E o) {
if(index == objects.length) {
E[] newObjects = (E[])new Object[objects.length*2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects = newObjects;
}
objects[index] = o;
index ++;
}
public int size() {
return index;
}
@Override
public Iterator_<E> iterator() {
return new ArrayListIterator();
}
private class ArrayListIterator<E> implements Iterator_<E> {
private int currentIndex = 0;
@Override
public boolean hasNext() {
if(currentIndex >= index) return false;
return true;
}
@Override
public E next() {
E o = (E)objects[currentIndex];
currentIndex ++;
return o;
}
}
}
主函数
package com.company.Iterator.v7;
/**
* v1:构建一个容器,可以添加对象
* v2:用链表来实现一个容器
* v3:添加容器的共同接口,实现容器的替换
* v4:如何对容器遍历呢?
* v4:用一种统一的遍历方式,要求每一个容器都要提供Iterator的实现类
* 作业:实现LinkedList的Iterator
* v6:JDK的容器实现
* v7:实现泛型版本
*/
public class Main {
public static void main(String[] args) {
Collection_<String> list = new ArrayList_<>();
for(int i=0; i<15; i++) {
list.add(new String("s" + i));
}
System.out.println(list.size());
//这个接口的调用方式:
Iterator_<String> it = list.iterator();
while(it.hasNext()) {
String o = it.next();
System.out.println(o);
}
}
}
运行结果
四、迭代器模式小结
4.1、优点
(1)迭代器模式简化了集合的接口,迭代子具备遍历功能,这样集合的接口就不需要重新实现遍历功能。
(2)每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。
(3)由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。
(4)更好的封装性,访问一个集合对象的元素,无需暴露容器内部表示。
4.2、缺点
(1)迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
(2)对于比较简单的遍历,使用迭代器模式显得较为繁琐,比如ArrayList直接就可以用for循环+get() 方法来遍历;
(3)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
4.3、适用场景
(1)访问一个集合对象的内容而无须暴露它的内部表示。
(2)需要为集合对象提供多种遍历方式。
(3)为遍历不同的聚合结构提供一个统一的接口。
PS:由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了。