什么是迭代器模式?
我们知道,Java中对于集合的遍历提供了一种很简单的实现——Iterator
类。
一般我们对集合遍历时,都会做如下程序:
List<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
Iterator<Integer> iterator = arrayList.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
这又被称为对集合的迭代,而Iterator
就是对应的迭代器。
所以,迭代器模式是什么?顾名思义,就是提供了一种方法顺序访问一个聚合对象中的各个元素,却又不暴露该对象的内部表示。
举个例子:
我们去医院都要先排号,这时所有排号的人就构成了一个列表,医生负责的就是从头开始,一个一个将人叫进来,当前谁正在进行诊治,下一个要诊治的人是谁,这个人是否是最后一个人,他不需要去考虑这些人是做什么的,他需要做的仅仅是遍历这些人,这就是迭代器模式。
也就是说,Java中的集合实现了这个迭代器后,他本身的所有状态就由迭代器自己来维护了,客户端进行调用的时候不需要知道集合内部如何进行的遍历操作,而仅仅需要发出指令:遍历下一个,是否结束,就可以对不同集合进行遍历。
迭代器模式的具体实现
主要以Java中集合的迭代为例。
首先Java维护了一个迭代器的根类型Iterator
:
package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
而所有的集合类的根类型Iterable
中声明了一个方法以用来返回迭代器对象:
package java.lang;
import java.util.Iterator;
public interface Iterable<T> {
Iterator<T> iterator();
}
这样,在集合类中只要维护其自身的iterator()
方法,就可以实现对不同集合类型的迭代。
拿ArrayList
类举例,看其重写的iterator()
方法:
public Iterator<E> iterator() {
return new Itr();
}
可以看到,其返回了一个Itr
对象,而Itr
正是ArrayList内部维护的一个类:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
// 实现代码
}
@SuppressWarnings("unchecked")
public E next() {
// 实现代码
}
public void remove() {
// 实现代码
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
// 实现代码
}
final void checkForComodification() {
// 实现代码
}
}
具体源码不深入解读了,这就是迭代器模式具体的实现框架方式。
注意点
在迭代时需要注意的是不能对底层集合的元素进行动态的删改,可以使用Iterator
中的remove()
方法对最后一个迭代器返回的元素进行删除,比如:
List<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
Iterator<Integer> iterator = arrayList.iterator();
iterator.next();
arrayList.add(4);
iterator.next();
这样是不行的,会抛出ConcurrentModificationException
异常。
拿ArrayList
来举例,在ArrayList
中维护了一个版本号,而在Iterator
的实现中对版本号进行了判断(就是上面的最后一个方法):
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount
是当前集合的版本号,而expectedModCount
是当前迭代器的版本号,在每次迭代器实例化时会初始化为modCount
(也就是执行iterator()
时),而更新集合时迭代器的版本号不会更新,此时就会抛出异常。
至于Java为什么要这样做,是因为集合的快速失败机制(fail-fast),这里主要是说设计模式的思想,所以不再深入。