type
status
date
slug
summary
tags
category
icon
password
一、迭代器模式概述
迭代器模式主要作用是解耦容器代码和遍历代码,使用设计模式的主要目的是解耦。
迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern)。
迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
迭代器模式结构:
- Collection 集合接口
- ConcreteCollection 具体集合实现类
- Iterator 迭代器接口
- ConcreteIterator 具体迭代器实现类
迭代器接口定义:
- 第一种定义:next() 函数用来将游标后移一位元素,currentItem() 函数用来返回当前游标指向的元素
- 第二种定义:返回当前元素与后移一位这两个操作,放到了同一个函数 next() 中完成(这也是 Jdk 的实现)
二、迭代器模式实现
简单实现
以 ArrayList 的迭代器为例,使用第一种迭代器接口定义实现:
简单优化:
- 为了封装迭代器的创建细节,可以在容器中定义一个 iterator() 方法,来创建对应的迭代器
- 为了能实现基于接口而非实现编程,还需要将这个方法定义在 List 接口中
三、迭代器模式的优势
遍历集合数据有三种方法:for 循环、foreach 循环、iterator 迭代器,其中 foreach 遍历底层也是迭代器,因此遍历集合只有两种方法。
- 解耦容器与遍历逻辑,降低容器复杂性。例如树和图的深度和广度遍历,可以实现 DFSIterator、BFSIterator 两个迭代器类。
- 容器和迭代器都提供了抽象的接口,方便我们在开发的时候,基于接口而非具体的实现编程。例如,要反向遍历链表,只需将 LinkedIterator 切换为 ReversedLinkedIterator 即可。
- 每个迭代器独享游标信息。多个不同的迭代器,同时对同一个容器进行遍历互不影响。
四、迭代遍历时增删元素
同样使用 ArrayList 基于数组的 List 的迭代器进行举例。
遍历时新增元素
- 新增的元素位于迭代器游标之前,数组整体后移,发生重复遍历
- 新增的元素位于迭代器游标之后,数组整体后移,不影响
遍历时删除元素
- 删除的元素位于迭代器游标之前,数组整体前移,丢失游标原来指向的元素
- 删除的元素位于迭代器游标之后,数组整体前移,不影响
解决遍历时集合的变化
- 一种是遍历的时候不允许增删元素
- 需要确定遍历开始和结束的时间点,该段事件内不允许增删
- 创建迭代器的时间点作为遍历开始的时间点
- 结束时间不好确定,遍历可能会中途结束 break,可在 Iterator 实现 finishIteration() 方法,遍历退出时由用户手动调用
- 另一种是增删元素之后让遍历报错(JDK 采用)
- ArrayList 中定义一个成员变量 modCount,记录集合被修改的次数,集合每调用一次增加或删除元素的函数,就会给 modCount 加 1。
- 迭代器中定义一个成员变量 expectedModCount ,记录创建迭代器时候的原始 modCount
- 当调用集合的 iterator() 函数来创建迭代器的时候,我们把 modCount 值传递给迭代器的 expectedModCount 成员变量
- 每次调用迭代器上的 hasNext()、next()、currentItem() 函数,我们都会检查集合上的 modCount 是否等于 expectedModCount
- 若两个值不相同,则说明集合已经改变了,集合发生了增删操作,之前创建的迭代器已经不能正确运行了,再继续使用就会产生不可预期的结果
- 作者:ITNXD
- 链接:https://blog.itnxd.eu.org/article/iterator-design-pattern-of-behavioral-patterns
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

