设计模式学习(十八):迭代器模式
作者:Grey
原文地址:
迭代器模式
迭代器模式是一种行为型模式。
迭代器最典型的应用是容器遍历
模仿 JDK 的容器,我们自定义一个容器并实现 iterator 方法;
首先,我们先定义一个容器接口
public interface Collection_<E> {
int size();
void add(E element);
Iterator_<E> iterator();
}
里面包括了一个 iterator 方法,所以每个实现这个容器接口的具体容器类型,都必须自定义 iterator 方法, 然后定义一个 Iterator 接口 Iterator_, 具体容器中可以增加一个内部类来专门实现这个接口,
比如我们的具体容器类是 ArrayList_
import static java.lang.System.arraycopy;
public class ArrayList_<E> implements Collection_<E> {
private E[] objects = (E[]) new Object[10];
private int index = 0;
@Override
public int size() {
return index;
}
@Override
public void add(E element) {
if (objects.length == size()) {
// 满了就扩容为原来的两倍
E[] newObjects = (E[]) new Object[objects.length * 2];
arraycopy(objects, 0, newObjects, 0, objects.length);
objects = newObjects;
}
objects[index] = element;
index++;
}
@Override
public Iterator_<E> iterator() {
return new ArrayListIterator_<>();
}
private class ArrayListIterator_<E> implements Iterator_<E> {
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < index;
}
@Override
public E next() {
E o = (E) objects[currentIndex];
currentIndex++;
return o;
}
}
}
我们主要看 ArrayListIterator_这个内部类,里面其实是实现了 Iterator_ 这个接口,所以 ArrayList_ 的遍历操作会执行这个内部类中的操作规则来对其进行遍历。
如何实现一个快照迭代器
我们可以在容器中,为每个元素保存两个时间戳,一个是添加时间戳 addTimestamp,一个是删除时间戳 delTimestamp。
当元素被加入到集合中的时候,我们将 addTimestamp 设置为当前时间,将 delTimestamp 设置成最大长整型值(Long.MAX_VALUE
)。
当元素被删除时,我们将 delTimestamp 更新为当前时间,表示已经被删除。
注意,这里只是标记删除,而非真正将它从容器中删除。
同时,每个迭代器也保存一个迭代器创建时间戳 snapshotTimestamp,也就是迭代器对应的快照的创建时间戳。
当使用迭代器来遍历容器的时候,只有满足
addTimestamp < snapshotTimestamp < delTimestamp
的元素,才是属于这个迭代器的快照。如果元素的
addTimestamp > snapshotTimestamp
说明元素在创建了迭代器之后才加入的,不属于这个迭代器的快照;
如果元素的
delTimestamp < snapshotTimestamp
说明元素在创建迭代器之前就被删除掉了,也不属于这个迭代器的快照。
这样就在不拷贝容器的情况下,在容器本身上借助时间戳实现了快照功能。
迭代器模式应用
- MyBatis 中的 DefaultCursor
它实现了 Cursor 接口,而且定义了一个成员变量 cursorIterator,其定义的类型为 CursorIterator 。继续查看 CursorIterator 类的源码实现,它是 DefaultCursor 的一个内部类,并且实现了 JDK 中的 Iterator 接口。