迭代器模式(下)
如何实现一个支持“快照”功能的迭代器?
所谓“快照”,指我们为容器创建迭代器的 时候,相当于给容器拍了一张快照(Snapshot)。之后即便我们增删容器中的元素,快照 中的元素并不会做相应的改动。而迭代器遍历的对象是快照而非容器,这样就避免了在使用 迭代器遍历的过程中,增删容器中的元素,导致的不可预期的结果或者报错。
方案一
在迭代器类中定义一个成员变量 snapshot 来存储快 照。每当创建迭代器的时候,都拷贝一份容器中的元素到快照中,后续的遍历操作都基于这 个迭代器自己持有的快照来进行。
1 | public class SnapshotArrayIterator<E> implements Iterator<E> { |
缺点:这个解决方案虽然简单,但代价也有点高。每次创建迭代器的时候,都要拷贝一份数据到快 照中,会增加内存的消耗。如果一个容器同时有多个迭代器在遍历元素,就会导致数据在内 存中重复存储多份。不过,庆幸的是,Java 中的拷贝属于浅拷贝,也就是说,容器中的对 象并非真的拷贝了多份,而只是拷贝了对象的引用而已。
方案二
我们可以在容器中,为每个元素保存两个时间戳,一个是添加时间戳 addTimestamp,一 个是删除时间戳 delTimestamp。
当元素被加入到集合中的时候,我们将 addTimestamp 设置为当前时间,将 delTimestamp 设置成最大长整型值。当元素被删除时,我们将 delTimestamp 更新为当前时间,表示已经被删除。
这里只是标记删除,而非真正将它从容器中删除。
同时,每个迭代器也保存一个迭代器创建时间戳 snapshotTimestamp,也就是迭代器对应 的快照的创建时间戳。当使用迭代器来遍历容器的时候,只有满足 addTimestamp<snapshotTimestamp<delTimestamp的元素,才是属于这个迭代器的快照。
可以将元素删除时间以及创建时间与迭代器的创建时间进行比较,来判断该元素是否是当前快照中的元素,这样就在不复制的情况下做到了快照,同时因为删除是标记删除,也不会造成影响,而且添加新的元素也不会被遍历到。
参考
《设计模式之美》