📌 PDF :AI人工智能 --- AI Agent智能体实战项目
大白话说Java设计模式-38-迭代器模式(业务实战篇):大白商城商品分页游标的"遍历封装"
📌 一句话本质:迭代器模式就是"统一遍历方式",把不同的集合(列表/树/分页)提供一致的访问接口。
🏷️ 标签:迭代器模式 / Java 设计模式 / 顺序访问 / 商品分页 / 大白商城 🎯 适合:初中级后端 / 经常被"多种集合遍历"困扰的工程师
目录
- 一、业务场景引入:为什么需要"统一遍历"?
- 二、反面教材:硬编码遍历的"灾难现场"
- [三、模式原理:迭代器的"迭代器 + 聚合"](#三、模式原理:迭代器的"迭代器 + 聚合")
- 四、实战代码:大白商城商品分页迭代器完整实现
- [五、迭代器 vs for 循环 / Stream](#五、迭代器 vs for 循环 / Stream)
- [六、JDK 的 Iterator 怎么用?](#六、JDK 的 Iterator 怎么用?)
- [七、Java 8 Stream 怎么借鉴迭代器?](#七、Java 8 Stream 怎么借鉴迭代器?)
- [八、工程决策 Checklist](#八、工程决策 Checklist)
- 九、与其他模式协作
- [十、本篇小结 + 下篇预告](#十、本篇小结 + 下篇预告)
一、业务场景引入:为什么需要"统一遍历"?
大白商城 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),每种结构遍历方式不同。
- 错误做法:业务方为每种结构写一种遍历
- 正确做法 :
- 每种集合提供 Iterator
- 业务方只通过 Iterator 遍历
- 统一接口
hasNext() / next()- 加新集合?实现 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 个核心要点)
- 本质 :迭代器 = 统一遍历接口 ,不暴露内部结构。
- 场景 :分页/树形/Map,多种集合统一遍历。
- 对比:迭代器 vs for(面向对象 vs 面向过程)/ 迭代器 vs Stream(外部 vs 内部)。
- JDK Iterator :
hasNext() / next(),ArrayList.Itr 是经典实现。 - 避坑:并发修改 / 多次 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 中的迭代器实现
下一篇我们会深入源码,回答三个问题:
- JDK 的
ArrayList.Itr怎么实现 fail-fast? - JDK 的
HashMap怎么用迭代器遍历? - Java Stream 怎么用迭代器实现?
并附完整的源码解读 + 流程图 + 大白商城的"抄作业"实践。
觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯