Java 设计模式・迭代器模式篇:从思想到代码实现
一、行为型模式在面向对象的世界里如何优雅地组织对象间的交互、分配职责是每一位开发者都会反复思考的问题。直接硬编码交互逻辑固然简单但当业务复杂度上升、对象协作关系变得错综复杂时这种方式就会让代码变得僵化、难以扩展。行为型设计模式正是为了解决这一痛点而诞生的一套思想体系。它们关注如何定义对象之间的通信方式和职责分配通过命令、迭代、观察者、策略等手段让对象间的协作更具灵活性、可复用性和可维护性。在 Java 开发中行为型模式主要包含以下 11 种经典实现模板方法模式 (Template Method)定义一个操作中的算法的骨架而将一些步骤延迟到子类中使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Java 设计模式・模板方法模式篇从思想到代码实现-CSDN博客策略模式 (Strategy)定义一系列的算法把它们一个个封装起来并且使它们可相互替换让算法独立于使用它的客户而变化。Java 设计模式・策略模式篇从思想到代码实现-CSDN博客命令模式 (Command)将一个请求封装为一个对象从而使你可以用不同的请求对客户进行参数化支持可撤销操作。Java 设计模式・命令模式篇从思想到代码实现-CSDN博客责任链模式 (Chain of Responsibility)将请求的发送者和接收者解耦使多个对象都有机会处理这个请求形成一条处理链。Java 设计模式・责任链模式篇从思想到代码实现-CSDN博客状态模式 (State)允许一个对象在其内部状态改变时改变它的行为对象看起来似乎修改了它的类。Java 设计模式・状态模式篇从思想到代码实现-CSDN博客观察者模式 (Observer)定义对象间的一种一对多的依赖关系当一个对象的状态发生改变时所有依赖它的对象都得到通知并被自动更新。Java 设计模式・观察者模式篇从思想到代码实现-CSDN博客中介者模式 (Mediator)用一个中介对象来封装一系列的对象交互使各对象不需要显式地相互引用从而降低耦合。Java 设计模式・中介者模式篇从思想到代码实现-CSDN博客迭代器模式 (Iterator)提供一种方法顺序访问一个聚合对象中的各个元素而又不暴露其内部的表示。访问者模式 (Visitor)表示一个作用于某对象结构中的各元素的操作它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。备忘录模式 (Memento)在不破坏封装性的前提下捕获一个对象的内部状态并在该对象之外保存这个状态以便以后恢复。解释器模式 (Interpreter)给定一个语言定义它的文法的一种表示并定义一个解释器这个解释器使用该表示来解释语言中的句子。二、迭代器模式2.1 介绍迭代器模式是一种行为型设计模式其官方定义为提供一种方法顺序访问一个聚合对象如集合、数组、链表等中的各个元素而又不暴露该对象的内部表示。简单来说迭代器模式的核心是将 “遍历集合元素” 的逻辑从集合类中抽离出来封装到独立的迭代器对象中—— 无需知道集合的底层结构数组、链表、哈希表只需通过迭代器的统一接口如next()、hasNext()就能按顺序访问元素实现 “遍历逻辑” 与 “集合本身” 的解耦。2.2 角色* 抽象聚合Aggregate角色定义存储、添加、删除聚合元素以及创建迭代器对象的接口。* 具体聚合ConcreteAggregate角色实现抽象聚合类返回一个具体迭代器的实例。* 抽象迭代器Iterator角色定义访问和遍历聚合元素的接口通常包含 hasNext()、next() 等方法。* 具体迭代器Concretelterator角色实现抽象迭代器接口中所定义的方法完成对聚合对象的遍历记录遍历的当前位置。三、代码实现为了方便理解本文采用中文定义类名3.1 抽象迭代器角色public interface 数字迭代器 { boolean hasNext(); Integer next(); }3.2 具体迭代器角色public class 具体数字迭代器 implements 数字迭代器 { private ListInteger list; private int index; public 具体数字迭代器(ListInteger list) { this.list list; } Override public boolean hasNext() { return index list.size(); } Override public Integer next() { return list.get(index); } }3.3 抽象聚合角色public interface 数字容器 { void addNumber(Integer number); void removeNumber(Integer number); 数字迭代器 getIterator(); }3.4 具体聚合角色public class 具体数字容器 implements 数字容器{ private ListInteger numbers new ArrayList(); Override public void addNumber(Integer number) { this.numbers.add(number); } Override public void removeNumber(Integer number) { this.numbers.remove(number); } Override public 数字迭代器 getIterator() { return new 具体数字迭代器(this.numbers); } }3.5 客户端public class 客户端 { public static void main(String[] args) { 数字容器 numbers new 具体数字容器(); numbers.addNumber(1); numbers.addNumber(2); numbers.addNumber(3); numbers.addNumber(4); numbers.removeNumber(3); 数字迭代器 iterator numbers.getIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }1 2 4 Process finished with exit code 0四、优缺点4.1 优点解耦遍历逻辑与聚合对象聚合对象只负责存储数据遍历逻辑顺序、倒序、过滤全部交给迭代器。即使聚合对象的内部存储结构从数组改成链表迭代器的调用方业务代码也无需修改。支持多种遍历方式可以为同一个聚合对象定义多个迭代器比如正序迭代器、倒序迭代器、过滤不及格学生的迭代器调用方按需选择无需修改聚合对象本身。符合 “单一职责原则”聚合对象专注于数据存储迭代器专注于遍历各自的职责更清晰代码维护更简单。4.2 缺点增加系统复杂度引入迭代器模式需要新增迭代器接口、具体迭代器类对于简单的聚合对象比如只是一个普通数组会 “过度设计”反而增加代码量。遍历效率可能降低自定义迭代器的遍历逻辑如果设计不当比如频繁遍历聚合对象的内部数据可能比直接遍历数组 / 集合的效率低尤其是对于短生命周期的聚合对象迭代器的创建和销毁会带来额外开销。对简单遍历不友好如果只是需要简单的 “从头到尾遍历”直接使用for循环比迭代器更简洁迭代器的抽象层反而增加了理解成本。五、适用场景5.1 适用场景需要统一遍历不同聚合对象的场景比如你的系统中有ArrayList、LinkedList、自定义的TreeCollection等多种数据容器希望用统一的方式遍历不用关心底层是数组、链表还是树迭代器模式可以封装不同容器的遍历差异。典型案例Java 集合框架中的Iterator接口ArrayList、HashMapkeySet/entrySet都实现了该接口遍历方式完全统一。需要隐藏聚合对象内部结构的场景聚合对象不想暴露自己的内部存储比如自定义的敏感数据容器只允许外部通过迭代器安全遍历避免外部直接操作内部数组 / 链表导致数据混乱。需要支持多种遍历方式的场景比如对一个OrderList既需要正序遍历、倒序遍历又需要按订单金额过滤遍历此时可以为OrderList创建多个迭代器业务代码按需调用。跨语言 / 框架的通用遍历需求比如在设计通用的组件库、数据展示框架时迭代器模式能让不同数据结构的遍历逻辑标准化降低调用方的学习成本。5.2 不适用场景聚合对象结构极简单比如固定长度的数组且只需要单一遍历方式对遍历性能要求极高比如高频次、大数据量的遍历迭代器的抽象层会带来额外开销临时的、一次性的遍历需求比如只在一个方法里遍历一次数组。六、对比学习6.1 与增强for循环for -each对比学习两者都用于遍历集合维度迭代器模式Iterator增强 for 循环for-each本质设计模式定义了遍历的抽象接口Java 语法糖底层依赖 Iterator集合或数组索引灵活性支持删除元素remove()、多类遍历正序 / 倒序只读遍历无法在遍历中删除元素会抛ConcurrentModificationException适用范围所有实现Iterable接口的聚合对象数组、实现Iterable的集合ArrayList/HashMap 等底层依赖自定义实现遍历逻辑集合遍历依赖 Iterator数组遍历依赖普通 for 循环6.2 与普通for循环直接遍历对比学习这是「抽象遍历」和「底层遍历」的对比核心是「封装性」和「耦合度」的差异。维度迭代器模式普通 for 循环数组 / 链表索引遍历耦合度与聚合对象内部结构解耦不用知道是数组 / 链表与内部结构强耦合数组用索引i链表用node.next通用性统一遍历接口换聚合对象无需改遍历代码聚合对象结构变了数组→链表遍历代码必须改代码复杂度抽象层多代码量稍大简单直接代码量少6.3 与枚举对比学习这是 Java 历史演进中的对比Enumeration 是迭代器模式的 “前身”迭代器是对它的优化。维度迭代器模式Iterator枚举Enumeration出现版本JDK 1.2JDK 1.0早期遍历接口方法名hasNext()、next()、remove()hasMoreElements()、nextElement()无删除方法设计思想支持失败快速检测并发修改抛异常无失败快速检测安全性低适用场景现代 Java 开发推荐仅兼容老旧代码如 Vector、Hashtable七、源码举例 java.util.Iterator7.1 角色对应迭代器模式角色JDK 中的具体体现核心方法迭代器接口Iteratorjava.util.IteratorhasNext()、next()、remove()聚合接口Aggregatejava.util.Collection通过Iterable接口iterator()来自Iterable具体聚合类ArrayList、LinkedList、HashMap等实现iterator()方法返回具体迭代器具体迭代器类集合内部的Itr内部类如ArrayList.Itr实现Iterator接口的所有方法7.2 迭代器接口public interface IteratorE { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException(remove); } default void forEachRemaining(Consumer? super E action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }7.3 聚合接口public interface IterableT { IteratorT iterator(); default void forEach(Consumer? super T action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } default SpliteratorT spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } }7.4 具体聚合角色public interface CollectionE extends IterableE { ... }7.5 具体迭代器角色ArrayList举例public class ArrayListE extends AbstractListE implements ListE { private transient Object[] elementData; // 底层数组 private int size; // 元素个数 // 1. 实现 Iterable 接口的 iterator() 方法返回具体迭代器 public IteratorE iterator() { return new Itr(); } // 2. 具体迭代器类内部类实现 Iterator 接口 private class Itr implements IteratorE { int cursor; // 下一个要访问的元素索引游标 int lastRet -1; // 上一个访问的元素索引初始-1 int expectedModCount modCount; // 快速失败检测 // 判断是否有下一个元素游标 集合大小 public boolean hasNext() { return cursor ! size; } // 获取下一个元素 SuppressWarnings(unchecked) public E next() { // 快速失败检测集合是否被修改并发修改 checkForComodification(); int i cursor; if (i size) // 超出范围抛异常 throw new NoSuchElementException(); Object[] elementData ArrayList.this.elementData; if (i elementData.length) throw new ConcurrentModificationException(); cursor i 1; // 游标后移 return (E) elementData[lastRet i]; // 返回当前元素记录lastRet } // 删除上一个访问的元素 public void remove() { if (lastRet 0) // 未访问过元素抛异常 throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); // 调用ArrayList的remove方法 cursor lastRet; // 游标回退因为删除后元素前移 lastRet -1; // 重置lastRet expectedModCount modCount; // 更新modCount } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } // 快速失败检测modCount 是集合修改次数expectedModCount 是迭代器创建时的次数 final void checkForComodification() { if (modCount ! expectedModCount) throw new ConcurrentModificationException(); } } }八、其他相关设计模式Java 设计模式・总结目录篇从思想到代码实现-CSDN博客