设计模式之迭代器模式

迭代器模式

迭代器模式

迭代器模式(Iterator Pattern)又称为游标模式(Cursor Pattern),它提供一种顺序访问集合/容器对象元素的方法,而又无须暴露集合内部表示。

迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构,属于行为型模式。

迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。

通用UML图

image-20210425230807024

从UML类图中,可以看到,迭代器模式主要包含四种角色:

抽象迭代器(Iterator):抽象迭代器负责定义访问和遍历元素的接口;

具体迭代器(Concretelterator):提供具体的元素遍历行为;

抽象容器(IAggregate):负责定义提供具体迭代器的接口;

具体容器(ConcreteAggregate):创建具体迭代器。

迭代器模式的应用场景

我们把多个对象聚在一起形成的总体称之为集合(Aggregate),集合对象是能够包容一组对象的容器对象。不同的集合其内部元素的聚合结构可能不同,而迭代器模式屏蔽了内部元素获取细节,为外部提供一致的元素访问行为,解耦了元素迭代与集合对象间的耦合,并且通过提供不同的迭代器,可以为同个集合对象提供不同顺序的元素访问行为,扩展了集合对象元素迭代功能,符合开闭原则。

迭代器模式适用于以下场景:

1、访问一个集合对象的内容而无需暴露它的内部表示;

2、为遍历不同的集合结构提供一个统一的访问接口。

通用代码

抽象迭代器(Iterator)

1
2
3
4
5
public interface Iterator<E>
{
E next();
boolean hasNext();
}

抽象容器(IAggregate)

1
2
3
4
5
6
public interface IAggregate<E>
{
boolean add(E e);
boolean remove(E e);
Iterator<E> iterator();
}

具体迭代器(Concretelterator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteIterator<E> implements Iterator<E>
{
private int cursor;
private List<E> list;
private E element;

public ConcreteIterator(List<E> list)
{
this.list = list;
}

@Override
public E next()
{
System.out.print("当前位置" + cursor + ": ");
E e = list.get(cursor);
cursor++;
return e;
}

@Override
public boolean hasNext()
{
return cursor <= list.size() - 1;
}
}

具体容器(ConcreteAggregate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ConcreteAggregate<E> implements IAggregate<E>
{
private List<E> list;

public ConcreteAggregate()
{
this.list = new ArrayList<>();
}

@Override
public boolean add(E e)
{
return list.add(e);
}

@Override
public boolean remove(E e)
{
return list.remove(e);
}

@Override
public Iterator<E> iterator()
{
return new ConcreteIterator<>(list);
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main
{
public static void main(String[] args)
{

ConcreteAggregate<String> concreteAggregate = new ConcreteAggregate<>();
concreteAggregate.add("a");
concreteAggregate.add("b");
concreteAggregate.add("c");
concreteAggregate.add("d");

System.out.println("遍历--------");
print(concreteAggregate);
System.out.println();
System.out.println("删除后遍历--------");
concreteAggregate.remove("c");
print(concreteAggregate);

}

public static void print(ConcreteAggregate<String> concreteAggregate)
{
Iterator<String> iterator = concreteAggregate.iterator();
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}

输出

1
2
3
4
5
6
7
8
9
10
遍历--------
当前位置0: a
当前位置1: b
当前位置2: c
当前位置3: d

删除后遍历--------
当前位置0: a
当前位置1: b
当前位置2: d

迭代器模式在源码中的应用

迭代器在JDK中最经典也是我们经常用的应用就是Iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();

/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();

/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @implSpec
* The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
*
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}

/**
* Performs the given action for each remaining element until all elements
* have been processed or the action throws an exception. Actions are
* performed in the order of iteration, if that order is specified.
* Exceptions thrown by the action are relayed to the caller.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* while (hasNext())
* action.accept(next());
* }</pre>
*
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

从上面代码中,看到两个主要的方法定义hasNext()next()方法,和我们自己写的完全一致。

另外,从上面的代码中,我们看到remove()方法实现似曾相识。其实是在组合模式中我们见到过,迭代器模式和组合模式,两者似乎存在一定的相似性。组合模式解决的是统一树形结构各层次访问接口,迭代器模式解决的是统一各集合对象元素遍历接口。虽然他们的适配场景不同,但核心理念是相通的。

具体实现可以源码,这里不再赘述。

迭代器模式的优缺点

优点:

1、多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的集合对象;

2、简化集合对象接口:迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无须关心具体迭代行为;

3、元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使的同种元素聚合结构可以有不同的迭代行为;

4、解耦迭代与集合:迭代器模式封装了具体的迭代算法,迭代算法的变化,不会影响到集合对象的架构。

缺点:

对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐。

在日常开发当中,我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供给我们的API完全够用。

打赏

请我喝杯咖啡吧~

支付宝
微信