定义:
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。
设计类图:
迭代器模式中涉及的角色:
- 抽象迭代器(Iterator)角色:负责定义遍历和访问元素的接口,是所有迭代器必须实现的接口。
- 具体迭代器(ConcreteIterator)角色:实现迭代器接口,记录目前遍历的位置,完成容器集合元素的遍历。
- 抽象聚合容器(Aggregate)角色:提供一个接口给所有的具体聚合容器使用,提供一个可以创建迭代器的方法createIterator(), 该方法必须返回一个迭代器对象。
- 具体聚合容器(ConcreteAggregate)角色:持有一个对象的集合,实现抽象容器接口的方法,该方法负责实例化并返回一个具体的迭代器实例。
示例代码:
public interface Aggregate {
/**返回一个迭代器对象*/
public Iterator createIterator();
public void add(Object object);
public void remove(Object object);
}
public class ConcreteAggregate implements Aggregate {
private List<Object> objectList = new ArrayList<>();
@Override
public Iterator createIterator() {
//创建并返回一个迭代器对象
return new ConcreteIterator(objectList);
}
@Override
public void add(Object object) {
objectList.add(object);
}
@Override
public void remove(Object object) {
objectList.remove(object);
}
}
public interface Iterator {
/**是否有下一个元素*/
public boolean hasNext();
/**返回下一个元素*/
public Object next();
/**移除当前元素*/
public void remove();
}
public class ConcreteIterator implements Iterator {
private List<Object> objectList = new ArrayList<>();
private int cursor = 0;
public ConcreteIterator(List<Object> objectList) {
this.objectList = objectList;
}
@Override
public boolean hasNext() {
return cursor < objectList.size();
}
@Override
public Object next() {
if (cursor < objectList.size()) {
return objectList.get(cursor++);
}
return null;
}
@Override
public void remove() {
objectList.remove(cursor);
}
}
public class Client {
public static void main(String[] args) {
//构造一个聚合对象并添加一些数据
Aggregate aggregate = new ConcreteAggregate();
aggregate.add("aaa");
aggregate.add("bbb");
aggregate.add("ccc");
//获取聚合对象的迭代器进行遍历操作
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
可以看到,在上面代码中具体的聚合对象类ConcreteAggregate没有提供直接访问元素的方法,它内部持有一个元素集合,但是对元素集合的访问权交给了Iterator对象。在具体的迭代器实现类ConcreteIterator中,实现了Iterator接口的三个必要方法hasNext()、next()、remove()
, 这样在客户端Client代码中对聚合对象中元素的遍历操作就可以借由对应的Iterator来完成。
选代器模式给你提供了一种方法:可以顺序访问一个聚合对象中的元素,而又不暴露其内部的表示。
选代器模式给你带来的另一个重要的优点是,迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面(也就是管理对象集合),而不必去理会遍历的事情。职责更加清晰明确,迭代器模式很好的遵循了单一职责的OO设计原则。
Java API中的迭代器模式
在java最新版本的jdk当中,几乎所有的集合对象都已经实现了迭代器模式。在java中所有的集合对象的最顶层接口都是Collection, 我们常见的List、Set、Queue、Stack、Vector等都实现了Collection接口:
我们看一下Collection接口的源码:
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object var1);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] var1);
boolean add(E var1);
boolean remove(Object var1);
boolean containsAll(Collection<?> var1);
boolean addAll(Collection<? extends E> var1);
boolean removeAll(Collection<?> var1);
default boolean removeIf(Predicate<? super E> var1) {
Objects.requireNonNull(var1);
boolean var2 = false;
Iterator var3 = this.iterator();
while(var3.hasNext()) {
if(var1.test(var3.next())) {
var3.remove();
var2 = true;
}
}
return var2;
}
boolean retainAll(Collection<?> var1);
void clear();
boolean equals(Object var1);
int hashCode();
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(this.spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(this.spliterator(), true);
}
}
可以看到Collection接口继承了Iterable接口,并且我们看到了Iterator的影子,再看一下Iterable接口的源码:
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
Iterator var2 = this.iterator();
while(var2.hasNext()) {
Object var3 = var2.next();
var1.accept(var3);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
}
}
在Iterable接口当中有一个方法Iterator<T> iterator();
没错,这个就是创建迭代器的方法。再看一下它的Iterator接口:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> var1) {
Objects.requireNonNull(var1);
while(this.hasNext()) {
var1.accept(this.next());
}
}
}
可以看到Iterator接口跟我们前面提到的迭代器模式中的示例代码相差无几,不过java中的迭代器接口都加了泛型,以便任何集合的实现类都可以方便的使用它。
因此在java中所有的Collection接口的集合子类,都可以像下面的代码这样使用迭代器去遍历:
ArrayList<String> list = new ArrayList<>();
list.add("1111");
list.add("2222");
list.add("3333");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
当然,从JDK1.5开始,都不需要你显示的使用Iterator接口,提供了新的for/in或者叫for/each写法,也就是我们日常写代码时常用的写法:
ArrayList<String> list = new ArrayList<>();
list.add("1111");
list.add("2222");
list.add("3333");
for (String str : list) {
System.out.println(str);
}
看到这段代码,你可能觉得,哦,原来迭代器就是for循环啊,so easy? 没错,我们几乎每天都在用迭代器模式!只是我们感知不到,因为JDK已经为我们提供好了良好的封装。除了迭代器,java的Collection接口也封装了很多其它操作,也是经常使用的。
有些书中提到,迭代器模式已经是一个没落的设计模式,因为许多友好的高级程序设计语言当中已经默认实现了迭代器模式,甚至想将它从设计模式中除名。但我不这样认为,作为一个几乎被所有高级程序设计语言集成到API当中的设计模式,迭代器模式一直延续使用至今,这说明它的优势带来的方便程度是很大的。如果我们现在想要对外部屏蔽一个集合元素的遍历细节,还是可以使用迭代器的,只不过机会比较少,也很少有人会去自己实现。但这并不意味着迭代器模式是一个没用的模式,相反它十分重要。
参考: