迭代器模式(Iterator Pattern)是一种行为设计模式,用于提供一种按着某种方式访问聚合对象中元素的方法,而不暴露该聚合对象底层实现。通过迭代器模式,客户端可以以统一的方式遍历不同集合(如数组、链表等),无需关心集合的内部表示。

适用场景

  • 简化复杂数据结构的访问
    客户端只关系元素的遍历,不需要了解复杂的集合实现,将针对集合元素的遍历逻辑与集合的具体实现解耦。
  • 提供多种遍历与访问方式
    针对同一集合提供多方式(正序、逆序、跳步等)对集合中的元素进行访问。
  • 集合类型的多样化
    客户端以一致的方式处理不同类型的集合,而无需关心不同类型集合内部的实现。修改集合类型时,客户端中针对集合遍历的代码无需修改,符合开闭原则。

UML图

上图中各角色说明如下表:

角色 功能说明
迭代器接口:Iterator 迭代器约束,定义了需要实现的遍历方式。
迭代器实现:ConcreteIterator 对迭代器定义的遍历方式的具体实现,需要感知聚合类的内部具体实现。
聚合接口:IAggregate 定义了产生迭代器角色的接口,根据业务需要返回不同的迭代器。
聚合实现:ConcretdAggregate 负责实现聚合接口约束,创建具体的迭代器实例。同时可以根据业务需要返回不同类型的迭代器实例。
客户端:client 使用聚合与迭代器完成业务需要的遍历逻辑。

迭代器模式的关键在于实现Iterator与IAggregate中的关键方法:

  • Iterator接口:
    • boolean hasNext():用于判断是否还有下一个元素。
    • E next():获取下一个元素。
  • IAggregate接口:
    • Iterator createIterator():创建一个迭代器对象。

实例分析

在java的集合框架JUC中,很多集合类(如ArrayList、LinkedList、HashSet等)都提供了iterator方法,用于返回Iterator实例。下面以ArrayList源码进行分析。

实例UML图

代码实现

在ArrayList的整个继承链中,通过最顶层的Iterable接口进行了约束。该接口中定义了iterator方法,用于返回Iterator实例。Collection接口通过对Iterable进行继承,实现对所有集合类的约束。Iterable接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Iterable<T> {

Iterator<T> iterator();

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}

为了避免Collection接口在后续迭代的过程,影响到List相关接口对于Iterator的约束,因此List接口中同样定义了iterator方法,用于产生Itrator实例。同时为了实现针对List个性化的遍历需求,定义了ListIterator,实现了向前遍历的功能,同时增加了set与add相关方法。ListIterator接口仅适用于List类型的约束,如ArrayList、LinkedList等。ListIterator定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
// 前向遍历
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}

由于每一种具体的List类型的内部集合实现均不同,比如ArrayList内部使用数组,LinkedList内部使用链表。因此针对List的每一种具体类型,在其内部均定义了Iterator与ListIterator的具体实现。如Itr与ListItr实例的定义。业务在使用的List进行遍历时,面相接口编程,无需关系底层具体的迭代器实现逻辑。进行List类型的替换,也不会影响到业务遍历的逻辑。这也就是为什么迭代器模式实现复杂,还要引入该模式的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void iterateVerify() {
// 此处的ArrayList可以替换成LinkedList,不影响后面的迭代逻辑
// List<String> list = new LinkedList<>();
List<String> list = new ArrayList<>();
// 集合数据添加
list.add("a");
list.add("b");

// 后向遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 前向遍历
ListIterator<String> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
}