【西瓜带你学设计模式 | 第十六期 - 迭代器模式】迭代器模式 —— 统一遍历实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 迭代器模式是什么?](#1. 迭代器模式是什么?)
    • [2. 迭代器模式解决什么问题?](#2. 迭代器模式解决什么问题?)
    • [3. 核心结构](#3. 核心结构)
      • [3.1 Iterator(抽象迭代器)](#3.1 Iterator(抽象迭代器))
      • [3.2 ConcreteIterator(具体迭代器)](#3.2 ConcreteIterator(具体迭代器))
      • [3.3 Aggregate(抽象集合)](#3.3 Aggregate(抽象集合))
      • [3.4 ConcreteAggregate(具体集合)](#3.4 ConcreteAggregate(具体集合))
    • [4. 实现思路](#4. 实现思路)
    • [5. 示例](#5. 示例)
      • [5.1 Iterator:抽象迭代器](#5.1 Iterator:抽象迭代器)
      • [5.2 Aggregate:抽象集合](#5.2 Aggregate:抽象集合)
      • [5.3 ConcreteAggregate:具体集合------书架](#5.3 ConcreteAggregate:具体集合——书架)
      • [5.4 ConcreteIterator:具体迭代器](#5.4 ConcreteIterator:具体迭代器)
      • [5.5 Client:客户端遍历](#5.5 Client:客户端遍历)
    • [6. 优缺点](#6. 优缺点)
      • [6.1 优点](#6.1 优点)
      • [6.2 缺点](#6.2 缺点)
    • [7. 和其他模式怎么区分?](#7. 和其他模式怎么区分?)
      • [7.1 迭代器 vs 访问者](#7.1 迭代器 vs 访问者)
      • [7.2 迭代器 vs 组合模式](#7.2 迭代器 vs 组合模式)
      • [7.3 迭代器 vs 策略模式](#7.3 迭代器 vs 策略模式)
    • [8. 适用场景](#8. 适用场景)
    • [9. Java 中的实际应用](#9. Java 中的实际应用)
    • [10. 总结](#10. 总结)

前言

在日常开发中,我们经常需要遍历一个集合里的所有元素,比如:

  • 遍历一棵树的所有节点(前序/中序/后序)
  • 遍历一个自定义列表、链表、图结构
  • 遍历数据库查询结果集
  • 遍历文件目录下的所有文件

这些场景有一个共同点:集合的内部结构各不相同,但客户端只想一个一个地拿到元素,不想关心底层是数组、链表还是树。

迭代器模式(Iterator Pattern)要解决的核心就是:

提供一种统一的方式,顺序访问集合中的每个元素,而不暴露集合的内部表示。


1. 迭代器模式是什么?

迭代器模式是一种行为型设计模式,它将"遍历逻辑"从集合本身中抽离出来,封装到一个独立的迭代器对象中,从而做到:

  • 客户端用统一的方式(hasNext() / next())遍历任何集合
  • 集合的内部结构可以自由变化,不影响遍历代码
  • 同一个集合可以同时存在多个独立的遍历过程

2. 迭代器模式解决什么问题?

  1. 集合内部结构复杂(数组、链表、树、图......),不希望把遍历细节暴露给调用方
  2. 希望对不同类型的集合提供统一的遍历接口
  3. 需要支持多种遍历方式(正序、倒序、过滤遍历等),且不想在集合类里堆一堆遍历方法
  4. 希望同一集合能被多个迭代器同时遍历,互不干扰

如果发现客户端代码里到处写 for (int i = 0; i < list.size(); i++) 这种依赖集合内部索引的遍历,而且集合结构还可能换,就很适合迭代器模式。


3. 核心结构

3.1 Iterator(抽象迭代器)

定义遍历集合所需的统一方法,例如 hasNext()next()

3.2 ConcreteIterator(具体迭代器)

实现迭代器接口,持有对具体集合的引用,维护当前遍历位置等状态。

3.3 Aggregate(抽象集合)

定义创建迭代器的方法,例如 createIterator()

3.4 ConcreteAggregate(具体集合)

实现集合接口,返回与自身匹配的具体迭代器实例。


4. 实现思路

  1. 定义 Iterator 接口(统一 hasNext() + next() 方法签名)
  2. 定义 Aggregate 接口(统一 createIterator() 方法)
  3. ConcreteAggregate(具体集合,存储元素)
  4. ConcreteIterator(具体迭代器,知道如何遍历对应集合)
  5. 客户端只跟 IteratorAggregate 接口交互,不关心集合内部结构

5. 示例

自定义书架集合,用迭代器遍历所有书籍

5.1 Iterator:抽象迭代器

java 复制代码
public interface Iterator<T> {
    boolean hasNext();
    T next();
}

5.2 Aggregate:抽象集合

java 复制代码
public interface Aggregate<T> {
    Iterator<T> createIterator();
}

5.3 ConcreteAggregate:具体集合------书架

java 复制代码
public class BookShelf implements Aggregate<String> {
    private final String[] books;
    private int count = 0;

    public BookShelf(int capacity) {
        this.books = new String[capacity];
    }

    public void addBook(String name) {
        books[count++] = name;
    }

    public String getBookAt(int index) {
        return books[index];
    }

    public int getSize() {
        return count;
    }

    @Override
    public Iterator<String> createIterator() {
        return new BookShelfIterator(this);
    }
}

5.4 ConcreteIterator:具体迭代器

java 复制代码
public class BookShelfIterator implements Iterator<String> {
    private final BookShelf bookShelf;
    private int index = 0;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return index < bookShelf.getSize();
    }

    @Override
    public String next() {
        String book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

5.5 Client:客户端遍历

java 复制代码
public class Client {
    public static void main(String[] args) {
        BookShelf shelf = new BookShelf(5);
        shelf.addBook("设计模式");
        shelf.addBook("重构");
        shelf.addBook("代码整洁之道");
        shelf.addBook("Effective Java");

        Iterator<String> it = shelf.createIterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

会发现:

  • 客户端完全不知道书架内部用的是数组------哪天换成 ArrayList 或链表,遍历代码一行不用改
  • 遍历逻辑被封装在 BookShelfIterator 里,书架类本身保持干净
  • 如果想加一个"倒序迭代器",只需新增一个 ReverseBookShelfIterator,不动现有代码

6. 优缺点

6.1 优点

  1. 统一遍历接口 :不管底层是数组、链表还是树,客户端用同一套 hasNext/next
  2. 解耦集合与遍历:集合只管存储,迭代器只管遍历,职责清晰
  3. 支持多种遍历方式:同一集合可以提供正序、倒序、过滤等不同迭代器
  4. 符合开闭原则:新增遍历方式只需新增迭代器类,不改集合代码

6.2 缺点

  1. 类数量增加:每种集合通常需要配一个迭代器类
  2. 对于简单集合(如普通数组、ArrayList),直接用 for-each 就够了,引入迭代器模式反而过度设计
  3. 迭代过程中如果集合被修改,可能引发并发问题(Java 的 ConcurrentModificationException 就是典型例子)

7. 和其他模式怎么区分?

7.1 迭代器 vs 访问者

  • 迭代器:关注"如何逐个访问元素",不关心对元素做什么操作
  • 访问者:关注"对不同类型的元素执行不同操作",遍历本身可能借助迭代器完成

7.2 迭代器 vs 组合模式

  • 组合模式:把对象组织成树形结构,统一叶子和容器的接口
  • 迭代器:经常和组合模式搭配使用------组合模式定义结构,迭代器负责遍历这棵树

7.3 迭代器 vs 策略模式

  • 策略模式:封装的是"算法/规则",运行时替换计算逻辑
  • 迭代器模式:封装的是"遍历方式",运行时替换遍历逻辑

本质上迭代器可以看作一种特化的策略------"遍历策略"。


8. 适用场景

满足以下情况时,迭代器模式很合适:

  • 集合内部结构复杂或可能变化,不希望暴露给客户端
  • 需要为同一集合提供多种遍历方式
  • 希望用统一接口遍历不同类型的集合
  • 需要支持同时进行多个独立的遍历(每个迭代器维护自己的游标)

9. Java 中的实际应用

其实你每天都在用迭代器模式------Java 的 java.util.Iterator 和增强 for 循环就是它的标准实现:

java 复制代码
List<String> list = List.of("A", "B", "C");

// 显式使用迭代器
java.util.Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

// for-each 语法糖,底层就是迭代器
for (String s : list) {
    System.out.println(s);
}

任何实现了 Iterable 接口的类都能用 for-each,这就是迭代器模式在语言层面的落地。


10. 总结

迭代器模式通过"抽象迭代器 + 具体迭代器 + 抽象集合 + 具体集合",将遍历逻辑从集合中解耦出来。客户端只需通过统一的 hasNext/next 接口逐个获取元素,无需了解集合内部是数组、链表还是树,从而让集合结构和遍历方式各自独立变化。

相关推荐
中国胖子风清扬1 小时前
基于GPUI框架构建现代化待办事项应用:从架构设计到业务落地
java·spring boot·macos·小程序·rust·uni-app·web app
Sirens.1 小时前
七大经典排序算法:原理、实现与复杂度分析
java·数据结构·算法·排序算法
万邦科技Lafite1 小时前
通过淘宝关键词API接口批量获取商品信息指南
java·前端·javascript
Seven971 小时前
【从0到1构建一个ClaudeAgent】规划与协调-任务系统
java
算.子1 小时前
【Spring AI 实战】四、OpenAI / Anthropic / Azure——多模型适配与自动配置原理
后端·ai编程
Cobyte2 小时前
从网关的角度理解并实现一个 Mini OpenClaw
后端·aigc·ai编程
Shadow(⊙o⊙)2 小时前
C中 memset enum malloc fputc fgetc fgets fread fwrite rewind指针回退
java·c语言·数据库
我命由我123452 小时前
Android buildSrc 模块问题:Gradle 的类 DefaultProject 被错误地尝试转换成 Apache Ant 的 Project 类
android·java·java-ee·kotlin·android jetpack·android-studio·android runtime
北风toto2 小时前
IDEA设置自定义注释(已亲测),新创建类的注释,在方法上自定义注释,设置注释签名
java·intellij-idea·策略模式