迭代器模式的优缺点是什么?
简化版
迭代器模式的优点是隐藏集合内部结构、统一遍历接口、支持多个独立遍历过程和多种遍历方式;缺点是会增加额外类或对象,简单场景可能显得复杂,遍历期间修改集合也需要额外处理。
详细版
迭代器模式带来的核心收益是解耦:
- 客户端不依赖集合内部结构。
- 遍历逻辑从集合和业务代码中分离。
- 不同集合可以用统一方式访问。
- 同一集合可以创建多个迭代器。
代价是结构上多了一层迭代器对象。如果集合很简单,且只有固定一处遍历,直接循环可能更清楚。此外,迭代期间修改集合容易引发一致性问题,需要遵守迭代器约定。
完整版教学
一、优点:保护封装性
集合对象不需要暴露内部数组、链表节点或树结构。
客户端只通过迭代器访问元素。这样集合内部实现改变时,外部遍历代码不一定需要修改。
这对维护长期演进的数据结构很重要。
二、优点:统一遍历接口
不同集合结构可以提供相同的遍历接口。
数组集合、链表集合、集合树都可以返回 Iterator。客户端只写一套遍历逻辑。
统一接口降低了使用成本,也让上层框架更容易处理不同集合。
三、优点:支持多个遍历过程
每个迭代器对象都有自己的游标。
同一个集合可以同时创建多个迭代器,各自遍历,互不影响。比如一个外层循环和一个内层循环可以分别持有不同迭代器。
如果游标放在集合对象里,就很难支持这种独立遍历。
四、优点:支持不同遍历策略
同一个聚合对象可以返回不同迭代器。
例如树结构可以有:
- 深度优先迭代器。
- 广度优先迭代器。
- 前序迭代器。
- 后序迭代器。
遍历策略独立封装后,集合类本身不会越来越臃肿。
五、缺点:增加对象和代码层次
迭代器模式会引入额外对象。对于非常简单的数组遍历,写迭代器可能显得啰嗦。
设计时要看是否真的需要隐藏结构、复用遍历逻辑或支持多种遍历。如果没有这些需求,普通循环就足够。
六、缺点:修改集合时要谨慎
遍历过程中修改集合是迭代器最容易出问题的地方。
在 Java 集合中,如果用集合自身的 add() 或 remove() 修改结构,可能触发 ConcurrentModificationException。
需要删除当前元素时,应使用迭代器自己的 remove(),或者选择并发集合、快照集合等适合场景的结构。
七、缺点:复杂遍历可能仍然难以抽象
有些遍历过程和业务规则高度绑定,例如遍历时还要根据多个外部状态动态跳转、回退、修改优先级。
这种情况下,单纯迭代器接口可能不够表达,需要结合访问者、策略、生成器或专门的查询接口。
八、常见误区与追问
迭代器以额外对象和状态管理换取统一遍历接口。客户端面对数组、链表、树的3种内部结构都可使用 hasNext/next,不需知道索引或节点链接;代价是每次遍历创建游标,并且复杂删除与并发修改语义必须定义。对性能敏感的基本类型循环还可能产生装箱成本。
| 检查维度 | 判定依据 |
|---|---|
| 主要收益 | 隐藏存储结构,统一顺序访问 |
| 主要代价 | 游标对象、边界和修改规则增加 |
Client -> Iterator;Collection 内部结构保持隐藏
心法:迭代器不是让遍历消失,而是把遍历状态从客户端移走。
- 误区:迭代器一定比下标循环慢。 取决于结构;链表按下标遍历反而可能退化为 O(n²)。
- 追问:为什么支持多个遍历策略? 同一集合可返回正序、逆序、过滤等不同 Iterator。
- 误区:隐藏内部结构就不能高效遍历。 集合可以提供与自身结构匹配的游标实现。
- 追问:内存成本通常是什么? 每次遍历至少保存游标、版本和可能的栈或队列状态。
- 追问:何时直接循环更合适? 局部固定数组且无需抽象多种集合时,简单循环更清楚。
九、加强记忆
迭代器模式的优点是“封装结构、统一走法、独立游标”,缺点是“多一层对象、修改时小心、简单场景可能过度”。判断是否使用,要看遍历逻辑是否值得被抽出来。