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

文章目录

    • 前言
    • [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 接口逐个获取元素,无需了解集合内部是数组、链表还是树,从而让集合结构和遍历方式各自独立变化。

相关推荐
书源丶3 小时前
三十六、File 类与 IO 流基础——文件操作的「第一步」
java
刀法如飞3 小时前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
swipe3 小时前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp3 小时前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端
hERS EOUS3 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
DFT计算杂谈3 小时前
wannier90 参数详解大全
java·前端·css·html·css3
LucianaiB4 小时前
我用飞书多维表做了一个 AI 活动推荐智能体:每天自动催我别错过截止日期!
后端
marsh02064 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
张健11564096484 小时前
临界区和同一线程上锁
java·开发语言·jvm