大白话说Java设计模式-38-迭代器模式(业务实战篇)

📌 PDF :AI人工智能 --- AI Agent智能体实战项目

大白话说Java设计模式-38-迭代器模式(业务实战篇):大白商城商品分页游标的"遍历封装"

📌 一句话本质:迭代器模式就是"统一遍历方式",把不同的集合(列表/树/分页)提供一致的访问接口。

🏷️ 标签:迭代器模式 / Java 设计模式 / 顺序访问 / 商品分页 / 大白商城 🎯 适合:初中级后端 / 经常被"多种集合遍历"困扰的工程师


目录


一、业务场景引入:为什么需要"统一遍历"?

大白商城 2024 年商品中心遇到一个经典问题:

"商品有 4 种集合(List/分页/树形菜单/Map),每种遍历方式不一样,业务方要写 4 套代码!"

我打开代码一看:

java 复制代码
/**
 * ❌ 大白商城老的商品遍历代码
 */
public class GoodsServiceV0 {

    public void processList(List<Goods> goodsList) {
        // ❌ 列表遍历
        for (int i = 0; i < goodsList.size(); i++) {
            Goods goods = goodsList.get(i);
            processOne(goods);
        }
    }

    public void processPage(PageResult<Goods> page) {
        // ❌ 分页遍历
        for (int i = 0; i < page.getRecords().size(); i++) {
            Goods goods = page.getRecords().get(i);
            processOne(goods);
        }
    }

    public void processTree(GoodsCategory root) {
        // ❌ 树形遍历
        processOne(root.getData());
        for (GoodsCategory child : root.getChildren()) {
            processTree(child);  // 递归
        }
    }

    public void processMap(Map<Long, Goods> goodsMap) {
        // ❌ Map 遍历
        for (Map.Entry<Long, Goods> entry : goodsMap.entrySet()) {
            processOne(entry.getValue());
        }
    }
}

这种代码的痛点

序号 问题 后果
4 套遍历代码 改一处要改 4 处
业务方要知道每种集合结构 复杂
分页 / 树形遍历容易错 边界 Bug
无法统一抽象 难维护

老板要的"业务方只写一种遍历代码",怎么搞?

答案就是------迭代器模式

1.1 大白话讲透迭代器

继续打比方:

场景:业务方要遍历商品(List/分页/树形/Map),每种结构遍历方式不同。

  • 错误做法:业务方为每种结构写一种遍历
  • 正确做法
    1. 每种集合提供 Iterator
    2. 业务方只通过 Iterator 遍历
    3. 统一接口 hasNext() / next()
    4. 加新集合?实现 Iterator业务方零改动

迭代器 = 提供一种方法顺序访问聚合对象,不暴露内部表示

1.2 迭代器模式的 3 个真实场景

大白商城里,迭代器模式用在:

场景 "聚合" "迭代器"
商品分页 PageResult PageIterator
树形菜单 Category TreeIterator
文件夹遍历 Folder FolderIterator
数据库游标 ResultSet ResultSetIterator
Map 遍历 HashMap EntryIterator

任何"顺序访问集合"的场景,都用迭代器


二、反面教材:硬编码遍历的"灾难现场"

我们看 4 个反面教材,看它们是怎么一步步崩的。

2.1 反面教材 v1:for 循环分散

java 复制代码
/**
 * ❌ 反面教材 v1:for 循环分散
 */
for (int i = 0; i < list.size(); i++) { ... }
for (int i = 0; i < page.size(); i++) { ... }
for (TreeNode n : tree.children()) { ... }

翻车现场

序号 问题 后果
遍历方式不一致 改一处要改 N 处
边界容易错 OOB 异常

2.2 反面教材 v2:下标硬编码

java 复制代码
/**
 * ❌ 反面教材 v2:下标硬编码
 */
for (int i = 0; i < list.size(); i++) {
    Goods g = list.get(i);
    // 业务逻辑
}

翻车现场

序号 问题 后果
暴露内部结构 改数据结构改遍历
不通用 List / Set 都要写

2.3 反面教材 v3:递归遍历难调试

java 复制代码
/**
 * ❌ 反面教材 v3:递归遍历
 */
public void processTree(GoodsCategory node) {
    process(node);
    for (GoodsCategory child : node.getChildren()) {
        processTree(child);  // 递归
    }
}

翻车现场

序号 问题 后果
递归深 StackOverflow
难调试 难追踪

2.4 反面教材 v4:完全没抽象

java 复制代码
/**
 * ❌ 反面教材 v4:完全没抽象
 */
public class GoodsServiceV4 {
    public void process() {
        // 1000 行所有逻辑混在一起
    }
}

翻车现场

序号 问题 后果
无法复用 改一处牵动全身
无法测试 单元测试跑不动

2.5 4 个反面教材的共同病根

痛点 反模式方案能不能解决?
统一遍历接口 ❌ 全部没有
多种集合通用 ❌ 全部要写
不暴露内部结构 ❌ 全部暴露
可替换 ❌ 全部写死

必须上迭代器模式


三、模式原理:迭代器的"迭代器 + 聚合"

3.1 迭代器的 2 个核心概念

迭代器(Iterator)

  • 提供顺序访问接口
  • 不暴露集合内部表示
  • hasNext() / next() / remove()

聚合(Aggregate)

  • 创建迭代器
  • 包含一组对象
  • iterator() 方法

大白商城例子

概念 例子
迭代器接口 GoodsIterator
具体迭代器 PageIterator / TreeIterator / ListIterator
聚合接口 GoodsAggregate
具体聚合 GoodsList / GoodsPage / GoodsTree

3.2 迭代器的 4 个核心角色

角色 职责 例子
迭代器接口(Iterator) 定义遍历接口 GoodsIterator
具体迭代器(Concrete Iterator) 实现遍历 PageIterator
聚合接口(Aggregate) 定义创建迭代器 GoodsAggregate
具体聚合(Concrete Aggregate) 实现创建迭代器 GoodsPage

3.3 一张图看懂迭代器

复制代码
GoodsAggregate(聚合接口)
  │
  │ iterator()
  │
  ├── GoodsList(具体聚合)
  │     └── iterator() → ListIterator
  │
  ├── GoodsPage(具体聚合)
  │     └── iterator() → PageIterator
  │
  └── GoodsTree(具体聚合)
        └── iterator() → TreeIterator

3.4 迭代器的"灵魂三问"

Q1:迭代器 vs for 循环,区别是什么?

答:

  • 迭代器面向对象(封装遍历)
  • for 循环面向过程(下标遍历)
  • 迭代器不暴露内部for 循环依赖集合结构
  • 迭代器统一接口 ,for 循环因集合而异

Q2:迭代器 vs Stream,区别是什么?

答:

  • 迭代器手动遍历(hasNext/next)
  • Stream函数式遍历(filter/map/reduce)
  • 迭代器命令式Stream 声明式
  • 迭代器支持外部迭代Stream 内部迭代

Q3:迭代器模式的核心价值是什么?

答:

  • 统一遍历接口(hasNext/next)
  • 不暴露内部结构
  • 支持多种遍历(正序/反序/分页)
  • 可替换(同接口不同实现)

3.5 迭代器的 3 种实现方式

方式 特点 适用
经典迭代器 接口 + 实现 标准
增强 for 循环 语法糖 简单
Stream 函数式 复杂

大白商城主推经典迭代器方式


四、实战代码:大白商城商品分页迭代器完整实现

下面是大白商城生产环境在用的迭代器实现,全套代码可直接复制到 IDEA 跑

4.1 项目环境与依赖

pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>

    <groupId>com.dabai.mall</groupId>
    <artifactId>mall-design-pattern-38</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>mall-design-pattern-38</name>
    <description>大白商城 - 设计模式 38 迭代器模式</description>

    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.2 迭代器接口:GoodsIterator

java 复制代码
package com.dabai.mall.iterator;

import java.util.function.Consumer;

/**
 * ✅ 迭代器接口:商品迭代器
 *
 * @author 大白商城技术团队
 */
public interface GoodsIterator<T> {

    /**
     * ✅ 是否有下一个
     */
    boolean hasNext();

    /**
     * ✅ 获取下一个
     */
    T next();

    /**
     * ✅ 默认 forEach
     */
    default void forEach(Consumer<? super T> action) {
        while (hasNext()) {
            action.accept(next());
        }
    }
}

4.3 聚合接口:GoodsAggregate

java 复制代码
package com.dabai.mall.iterator;

/**
 * ✅ 聚合接口
 * <p>
 * 创建迭代器
 */
public interface GoodsAggregate<T> {

    /**
     * ✅ 创建迭代器
     */
    GoodsIterator<T> iterator();
}

4.4 3 个具体迭代器

ListIterator

java 复制代码
package com.dabai.mall.iterator;

import java.util.List;

/**
 * ✅ 具体迭代器:列表迭代器
 */
public class ListIterator<T> implements GoodsIterator<T> {

    private final List<T> list;
    private int cursor = 0;

    public ListIterator(List<T> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return cursor < list.size();
    }

    @Override
    public T next() {
        if (!hasNext()) {
            throw new IllegalStateException("没有下一个元素");
        }
        return list.get(cursor++);
    }
}

PageIterator

java 复制代码
package com.dabai.mall.iterator;

import lombok.extern.slf4j.Slf4j;

/**
 * ✅ 具体迭代器:分页迭代器
 * <p>
 * 自动加载下一页
 */
@Slf4j
public class PageIterator<T> implements GoodsIterator<T> {

    /**
     * 当前页
     */
    private int currentPage = 1;
    /**
     * 每页大小
     */
    private final int pageSize;
    /**
     * 当前页数据
     */
    private java.util.List<T> currentData;
    /**
     * 当前页索引
     */
    private int currentIndex = 0;
    /**
     * 总数
     */
    private final long total;
    /**
     * 数据加载器
     */
    private final PageDataLoader<T> dataLoader;

    public PageIterator(int pageSize, long total, PageDataLoader<T> dataLoader) {
        this.pageSize = pageSize;
        this.total = total;
        this.dataLoader = dataLoader;
        this.currentData = dataLoader.load(currentPage, pageSize);
    }

    @Override
    public boolean hasNext() {
        // 还有当前页数据
        if (currentIndex < currentData.size()) {
            return true;
        }
        // 还有下一页
        return (long) currentPage * pageSize < total;
    }

    @Override
    public T next() {
        if (!hasNext()) {
            throw new IllegalStateException("没有下一个元素");
        }
        if (currentIndex >= currentData.size()) {
            // 加载下一页
            currentPage++;
            currentData = dataLoader.load(currentPage, pageSize);
            currentIndex = 0;
        }
        return currentData.get(currentIndex++);
    }

    /**
     * 数据加载器接口
     */
    @FunctionalInterface
    public interface PageDataLoader<T> {
        java.util.List<T> load(int page, int pageSize);
    }
}

TreeIterator

java 复制代码
package com.dabai.mall.iterator;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

/**
 * ✅ 具体迭代器:树形迭代器(广度优先)
 */
public class TreeIterator<T> implements GoodsIterator<T> {

    private final Deque<TreeNode<T>> queue = new ArrayDeque<>();

    public TreeIterator(TreeNode<T> root) {
        if (root != null) {
            queue.add(root);
        }
    }

    @Override
    public boolean hasNext() {
        return !queue.isEmpty();
    }

    @Override
    public T next() {
        if (!hasNext()) {
            throw new IllegalStateException("没有下一个元素");
        }
        // ✅ 广度优先
        TreeNode<T> node = queue.poll();
        if (node.getChildren() != null) {
            queue.addAll(node.getChildren());
        }
        return node.getData();
    }

    /**
     * 树节点
     */
    @lombok.Data
    @lombok.AllArgsConstructor
    public static class TreeNode<T> {
        private T data;
        private List<TreeNode<T>> children;
    }
}

TreeDFSIterator

java 复制代码
package com.dabai.mall.iterator;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * ✅ 具体迭代器:树形迭代器(深度优先)
 */
public class TreeDFSIterator<T> implements GoodsIterator<T> {

    private final Deque<TreeNode<T>.TreeNode<T>> stack = new ArrayDeque<>();

    public TreeDFSIterator(TreeNode<T> root) {
        if (root != null) {
            stack.push(root);
        }
    }

    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }

    @Override
    public T next() {
        if (!hasNext()) {
            throw new IllegalStateException("没有下一个元素");
        }
        // ✅ 深度优先
        TreeNode<T> node = stack.pop();
        if (node.getChildren() != null) {
            // 反转后入栈,保证顺序
            for (int i = node.getChildren().size() - 1; i >= 0; i--) {
                stack.push(node.getChildren().get(i));
            }
        }
        return node.getData();
    }
}

4.5 3 个具体聚合

GoodsList

java 复制代码
package com.dabai.mall.iterator;

import java.util.List;

/**
 * ✅ 具体聚合:商品列表
 */
public class GoodsList<T> implements GoodsAggregate<T> {

    private final List<T> data;

    public GoodsList(List<T> data) {
        this.data = data;
    }

    @Override
    public GoodsIterator<T> iterator() {
        return new ListIterator<>(data);
    }
}

GoodsPage

java 复制代码
package com.dabai.mall.iterator;

import lombok.AllArgsConstructor;

/**
 * ✅ 具体聚合:商品分页
 */
@AllArgsConstructor
public class GoodsPage<T> implements GoodsAggregate<T> {

    private final int pageSize;
    private final long total;
    private final PageIterator.PageDataLoader<T> dataLoader;

    @Override
    public GoodsIterator<T> iterator() {
        return new PageIterator<>(pageSize, total, dataLoader);
    }
}

GoodsTree

java 复制代码
package com.dabai.mall.iterator;

/**
 * ✅ 具体聚合:商品树
 */
public class GoodsTree<T> implements GoodsAggregate<T> {

    private final TreeIterator.TreeNode<T> root;

    public GoodsTree(TreeIterator.TreeNode<T> root) {
        this.root = root;
    }

    @Override
    public GoodsIterator<T> iterator() {
        return new TreeIterator<>(root);
    }
}

4.6 商品 DTO

java 复制代码
package com.dabai.mall.goods;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Goods {
    private Long id;
    private String name;
    private BigDecimal price;
    private Integer stock;
}

4.7 业务调用方

java 复制代码
package com.dabai.mall.service;

import com.dabai.mall.goods.Goods;
import com.dabai.mall.iterator.*;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ✅ 商品服务:迭代器模式的调用方
 * <p>
 * 业务方只通过 iterator() 遍历,不关心数据结构
 *
 * @author 大白商城技术团队
 */
@Service
public class GoodsService {

    /**
     * ✅ 业务方只写一种遍历代码
     */
    public void processAll(GoodsAggregate<Goods> aggregate) {
        // ✅ 统一接口
        GoodsIterator<Goods> iterator = aggregate.iterator();
        while (iterator.hasNext()) {
            Goods goods = iterator.next();
            log.info("处理商品: id={}, name={}", goods.getId(), goods.getName());
        }
    }

    public void processList() {
        // ✅ 列表遍历
        List<Goods> list = Arrays.asList(
                Goods.builder().id(1L).name("iPhone").build(),
                Goods.builder().id(2L).name("iPad").build()
        );
        processAll(new GoodsList<>(list));
    }

    public void processPage() {
        // ✅ 分页遍历
        GoodsPage<Goods> page = new GoodsPage<>(
                10, 100,  // 每页 10,总 100
                (p, size) -> {
                    // 实际查 DB
                    log.info("加载第 {} 页", p);
                    return new ArrayList<>();
                }
        );
        processAll(page);
    }

    public void processTree() {
        // ✅ 树形遍历
        TreeIterator.TreeNode<Goods> root = new TreeIterator.TreeNode<>(
                Goods.builder().id(1L).name("电子产品").build(),
                List.of(
                        new TreeIterator.TreeNode<>(
                                Goods.builder().id(2L).name("手机").build(), null),
                        new TreeIterator.TreeNode<>(
                                Goods.builder().id(3L).name("电脑").build(), null)
                )
        );
        processAll(new GoodsTree<>(root));
    }
}

4.8 单元测试

java 复制代码
package com.dabai.mall.iterator;

import com.dabai.mall.goods.Goods;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 迭代器模式完整单元测试
 */
class GoodsIteratorTest {

    @Test
    void testListIterator() {
        List<Goods> list = Arrays.asList(
                Goods.builder().id(1L).name("iPhone").build(),
                Goods.builder().id(2L).name("iPad").build(),
                Goods.builder().id(3L).name("Mac").build()
        );

        GoodsAggregate<Goods> aggregate = new GoodsList<>(list);
        GoodsIterator<Goods> iterator = aggregate.iterator();

        int count = 0;
        while (iterator.hasNext()) {
            Goods g = iterator.next();
            count++;
            assertNotNull(g);
        }
        assertEquals(3, count);
    }

    @Test
    void testPageIterator() {
        // ✅ 分页迭代器:自动加载下一页
        List<List<Goods>> pages = new ArrayList<>();
        pages.add(Arrays.asList(
                Goods.builder().id(1L).name("A").build(),
                Goods.builder().id(2L).name("B").build()
        ));
        pages.add(Arrays.asList(
                Goods.builder().id(3L).name("C").build()
        ));

        GoodsPage<Goods> page = new GoodsPage<>(
                2, 3,
                (p, size) -> pages.get(p - 1)
        );

        GoodsIterator<Goods> iterator = page.iterator();
        int count = 0;
        while (iterator.hasNext()) {
            Goods g = iterator.next();
            count++;
            assertNotNull(g);
        }
        assertEquals(3, count);
    }

    @Test
    void testTreeIterator() {
        // ✅ 树形迭代器:广度优先
        TreeIterator.TreeNode<Goods> root = new TreeIterator.TreeNode<>(
                Goods.builder().id(1L).name("电子产品").build(),
                List.of(
                        new TreeIterator.TreeNode<>(
                                Goods.builder().id(2L).name("手机").build(), null),
                        new TreeIterator.TreeNode<>(
                                Goods.builder().id(3L).name("电脑").build(),
                                List.of(
                                        new TreeIterator.TreeNode<>(
                                                Goods.builder().id(4L).name("笔记本").build(), null)
                                ))
                )
        );

        GoodsAggregate<Goods> aggregate = new GoodsTree<>(root);
        GoodsIterator<Goods> iterator = aggregate.iterator();

        int count = 0;
        while (iterator.hasNext()) {
            Goods g = iterator.next();
            count++;
            log.info("遍历到: {}", g.getName());
        }
        assertEquals(4, count);
    }

    @Test
    void testForEach() {
        // ✅ forEach 语法
        List<Goods> list = Arrays.asList(
                Goods.builder().id(1L).name("A").build(),
                Goods.builder().id(2L).name("B").build()
        );

        List<Long> ids = new ArrayList<>();
        new GoodsList<>(list).iterator().forEach(g -> ids.add(g.getId()));

        assertEquals(2, ids.size());
    }

    @Test
    void testEmptyIterator() {
        // ✅ 空集合迭代器
        GoodsAggregate<Goods> empty = new GoodsList<>(new ArrayList<>());
        assertFalse(empty.iterator().hasNext());
    }

    @Test
    void testIteratorConsistent() {
        // ✅ 多次调用 iterator() 独立
        List<Goods> list = Arrays.asList(
                Goods.builder().id(1L).name("A").build()
        );

        GoodsAggregate<Goods> aggregate = new GoodsList<>(list);
        GoodsIterator<Goods> it1 = aggregate.iterator();
        GoodsIterator<Goods> it2 = aggregate.iterator();

        assertTrue(it1.hasNext());
        assertTrue(it2.hasNext());
    }
}

五、迭代器 vs for 循环 / Stream

5.1 一张表看清区别

方式 范式 暴露内部 复杂度 适用
迭代器 面向对象 多种集合
for 循环 面向过程 简单列表
Stream 函数式 复杂处理

5.2 一个具体例子区分 3 种方式

大白商城

方式 例子
迭代器 分页 / 树形遍历
for 循环 普通 List
Stream 过滤 / 转换 / 聚合

5.3 决策树

复制代码
需要遍历集合?
├── 是
│   ├── 数据结构多样化?
│   │   ├── 是(分页/树形/Map)→ ✅ 迭代器
│   │   └── 否(普通 List)→ for 循环
│   ├── 需要复杂处理(filter/map)?
│   │   ├── 是 → ✅ Stream
│   │   └── 否 → 迭代器
└── 否 → 不需要

5.4 迭代器 vs for:易混淆

维度 迭代器 for 循环
范式 面向对象 面向过程
暴露 不暴露 暴露下标
通用性
性能 略低 略高

大白商城选型

场景 选哪个 原因
分页遍历 迭代器 跨页
树形遍历 迭代器 避免递归
普通 List for / 增强 for 简单
复杂处理 Stream 函数式

六、JDK 的 Iterator 怎么用?

6.1 Iterator:迭代器接口

源码位置

  • OpenJDK 17
  • src/java.base/share/classes/java/util/Iterator.java

核心源码

java 复制代码
/**
 * ✅ 迭代器接口
 */
public interface Iterator<E> {
    /**
     * ✅ 是否有下一个
     */
    boolean hasNext();

    /**
     * ✅ 获取下一个
     */
    E next();

    /**
     * 移除
     */
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    /**
     * forEach
     */
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

6.2 Iterable:可迭代接口

java 复制代码
/**
 * ✅ 可迭代接口
 */
public interface Iterable<T> {
    /**
     * ✅ 创建迭代器
     */
    Iterator<T> iterator();

    /**
     * forEach
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

6.3 ArrayList.Itr:具体迭代器

java 复制代码
/**
 * ✅ ArrayList 内部迭代器
 */
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    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];
    }
}

关键点

关键点 解释
cursor 当前游标
lastRet 上次返回位置
expectedModCount 防止并发修改
next() 边界检查

6.4 大白商城的"抄作业"

java 复制代码
/**
 * 借鉴 JDK Iterator:自定义迭代器
 */
public class SimpleIterator<T> implements Iterator<T> {

    private final List<T> list;
    private int cursor = 0;

    public SimpleIterator(List<T> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return cursor < list.size();
    }

    @Override
    public T next() {
        return list.get(cursor++);
    }
}

七、Java 8 Stream 怎么借鉴迭代器?

7.1 Stream 是什么?

Stream 是 Java 8 引入的函数式迭代

java 复制代码
/**
 * ✅ Stream:函数式迭代
 */
List<Goods> goods = ...;

// 内部迭代
List<String> names = goods.stream()
        .filter(g -> g.getPrice() > 100)
        .map(Goods::getName)
        .sorted()
        .toList();

7.2 Stream vs 迭代器

维度 Stream 迭代器
范式 内部迭代 外部迭代
写法 函数式 命令式
性能 可并行 单线程
状态 无状态 有 cursor

7.3 完整 Stream 调用链

复制代码
goods.stream()
        ↓
Stream<Goods>
        ↓
.filter(g -> g.getPrice() > 100)  // 过滤
        ↓
Stream<Goods>
        ↓
.map(Goods::getName)  // 转换
        ↓
Stream<String>
        ↓
.sorted()  // 排序
        ↓
Stream<String>
        ↓
.toList()  // 收集

7.4 大白商城的"抄作业"

java 复制代码
/**
 * 借鉴 Stream:商品处理
 */
public class GoodsStreamProcessor {

    public List<String> getExpensiveNames(List<Goods> goods) {
        // ✅ 借鉴 Stream:函数式处理
        return goods.stream()
                .filter(g -> g.getPrice().compareTo(new BigDecimal("100")) > 0)
                .map(Goods::getName)
                .sorted()
                .toList();
    }
}

八、工程决策 Checklist

8.1 ✅ 这 5 种情况,强烈建议用迭代器

序号 场景 原因
多种集合遍历 分页 / 树形 / Map
不暴露内部结构 解耦
统一遍历接口 业务方简洁
可替换迭代器 BFS / DFS
支持多种遍历 正序 / 反序

8.2 ❌ 这 5 种情况,绝对不要用迭代器

序号 场景 原因
只有 1 种集合 for 循环够用
业务极简 过度设计
追求性能 for 更快
只需要下标 直接 for
JDK Stream 够用 优先 Stream

8.3 ⚠️ 迭代器的 6 大常见坑

序号 表现 解决方案
并发修改 ConcurrentModificationException 单线程
多次 hasNext 状态变 缓存
游标错乱 多迭代器干扰 独立游标
资源未释放 游标 / 连接泄漏 try-with-resources
无限循环 死循环 边界检查
遍历中修改 数据不一致 快照

8.4 面试官视角:迭代器高频追问

Q1:迭代器 vs for 循环,区别是什么?

答:迭代器面向对象 (不暴露内部),for 循环面向过程 (依赖下标)。迭代器通用,for 循环特定

Q2:迭代器 vs Stream,区别是什么?

答:迭代器外部迭代 (手动 next),Stream 内部迭代 (声明式)。迭代器命令式,Stream 函数式

Q3:怎么避免 ConcurrentModificationException?

答:

  • fail-fast:迭代器检测到修改抛异常
  • 避免:遍历时不修改
  • 用 CopyOnWriteArrayList 等并发集合
  • 用 Stream:天然安全

九、与其他模式协作

9.1 迭代器 + 组合 = 树形迭代

java 复制代码
/**
 * ✅ 迭代器 + 组合:树形迭代
 */
public class TreeNode<T> {
    private T data;
    private List<TreeNode<T>> children;

    public Iterator<T> iterator() {
        return new TreeIterator<>(this);
    }
}

9.2 迭代器 + 工厂方法 = 多种迭代器

java 复制代码
/**
 * ✅ 迭代器 + 工厂
 */
public class IteratorFactory {

    public static <T> GoodsIterator<T> create(String type, List<T> data) {
        return switch (type) {
            case "list" -> new ListIterator<>(data);
            case "page" -> new PageIterator<>(10, data.size(), (p, s) -> data);
            default -> new ListIterator<>(data);
        };
    }
}

9.3 迭代器 + 享元 = 共享迭代器

9.4 大白商城模式协作全景图

复制代码
                    ┌──────────────┐
                    │  迭代器      │ ← 本篇
                    └──────┬───────┘
                           │
       ┌───────────┬───────┼───────┬───────────┐
       │           │       │       │           │
   ┌───▼───┐  ┌────▼───┐ ┌▼────┐ ┌▼─────┐  ┌───▼────┐
   │组合   │  │工厂方法│ │享元  │ │Stream│  │ 策略   │
   │(树)   │  │(创建) │ │(共享)│ │(函数)│  │ (多种) │
   └───────┘  └────────┘ └──────┘ └──────┘  └───────┘
   20 篇       04 篇     24 篇    Java 8    26 篇

十、本篇小结 + 下篇预告

10.1 本篇小结(5 个核心要点)

  1. 本质 :迭代器 = 统一遍历接口不暴露内部结构
  2. 场景 :分页/树形/Map,多种集合统一遍历
  3. 对比:迭代器 vs for(面向对象 vs 面向过程)/ 迭代器 vs Stream(外部 vs 内部)。
  4. JDK IteratorhasNext() / next(),ArrayList.Itr 是经典实现。
  5. 避坑:并发修改 / 多次 hasNext / 游标错乱 / 资源未释放。

10.2 一句话总结

迭代器不是"换种方式写 for 循环",是"用统一接口遍历多种集合" 。大白商城从 4 套遍历代码 → 1 套 iterator() / hasNext() / next()------加新集合类型只加 1 个 Iterator,业务方零改动

10.3 知识脑图

复制代码
迭代器模式
├── 2 个核心概念
│   ├── 迭代器(Iterator)
│   └── 聚合(Aggregate)
├── 4 大角色
│   ├── 迭代器接口(GoodsIterator)
│   ├── 具体迭代器(3 个)
│   ├── 聚合接口(GoodsAggregate)
│   └── 具体聚合(3 个)
├── 3 个具体迭代器
│   ├── ListIterator(列表)
│   ├── PageIterator(分页)
│   └── TreeIterator(树形)
├── 模式对比
│   ├── vs for 循环(面向对象 vs 过程)
│   ├── vs Stream(外部 vs 内部)
│   └── vs 增强 for(迭代器 vs 语法糖)
├── JDK Iterator
│   ├── Iterator 接口
│   ├── Iterable 接口
│   ├── ArrayList.Itr
│   └── HashMap 迭代器
└── 模式协作
    ├── + 组合(树形迭代)
    ├── + 工厂(创建迭代器)
    └── + Stream(函数式遍历)

10.4 下篇预告

第 39 篇【迭代器模式 - 源码剖析篇】:JDK 中的迭代器实现

下一篇我们会深入源码,回答三个问题:

  1. JDK 的 ArrayList.Itr 怎么实现 fail-fast?
  2. JDK 的 HashMap 怎么用迭代器遍历?
  3. Java Stream 怎么用迭代器实现?

并附完整的源码解读 + 流程图 + 大白商城的"抄作业"实践。


觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯

相关推荐
QQ_21696290961 小时前
【源码编号:project86570】SpringBoot电影院在线选座售票系统:电影排片、在线选座、订单购票、后台管理完整实战
java·spring boot·后端
谢亮_vipxieliang1 小时前
手机号验证技术方案:号段规则与正则表达式
java·服务器·spring boot·正则表达式·hibernate
wang09071 小时前
自己动手写一个tomcat之7监听器和过滤器
java·tomcat
梅孔立1 小时前
Pi-Agent 终极极简配置文档(Java+Vue+Python爬虫 4-5千文件项目)
java·vue.js·python
Query*2 小时前
Agent 开发之项目 AI Native 化:通过大模型与 RAG 赋予产品智能能力
java·人工智能·ai
孫治AllenSun2 小时前
【LangChain4J-03】Springboot 项目搭建框架
java·spring boot·后端
君顾12 小时前
AI新零售线上商城系统实战:架构设计与开发全流程指南
java·开发语言·零售
qq_485015212 小时前
MyBatis-Plus 3.x FieldStrategy 作用
java·数据库·mybatis
lhldsg2 小时前
课程排课系统实战指南:从数据库设计到算法调优全流程解析
java·数据库·算法·小程序