迭代器模式:集合遍历的统一之道

引言:集合遍历的演进之路

在软件开发中,集合遍历是我们每天都要面对的基础操作。从最初的数组索引遍历到现代的流式处理,我们经历了:

原始索引遍历
迭代器模式
内部迭代器
Stream API

然而,即使有了高级API,迭代器模式仍然是理解集合遍历本质的关键。它提供了一种统一的方式访问各种聚合对象中的元素,而无需暴露底层表示。本文将深入解析迭代器模式的原理、实现及高级应用场景。


一、模式定义与核心思想

1.1 官方定义

迭代器模式 (Iterator Pattern):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

1.2 设计哲学

客户端
迭代器接口
具体迭代器
聚合接口
具体聚合

核心原则

  1. 单一职责:将遍历逻辑与集合实现分离
  2. 开闭原则:新增集合类型不影响遍历代码
  3. 统一接口:为不同集合提供一致的遍历方式

二、模式结构解析

2.1 UML类图

创建
访问
«interface»
Iterator
+hasNext() : boolean
+next() : Object
+remove() : void
ConcreteIterator
-collection: Collection
-cursor: int
+hasNext()
+next()
+remove()
«interface»
Aggregate
+createIterator() : Iterator
ConcreteAggregate
-elements: Object[]
+createIterator()

2.2 关键角色

角色职责Java集合示例
Iterator定义遍历接口java.util.Iterator
ConcreteIterator实现具体遍历逻辑ArrayList.Itr
Aggregate定义创建迭代器接口java.lang.Iterable
ConcreteAggregate实现集合创建迭代器ArrayList

三、代码实战:自定义集合实现

3.1 场景描述

实现一个支持多种遍历方式的二维矩阵:

  • 行优先遍历(从左到右,从上到下)
  • 列优先遍历(从上到下,从左到右)
  • 对角线遍历

3.2 核心实现

// 迭代器接口
public interface MatrixIterator<T> {boolean hasNext();T next();
}// 矩阵接口
public interface Matrix<T> {int getRows();int getColumns();T get(int row, int col);MatrixIterator<T> rowMajorIterator();MatrixIterator<T> columnMajorIterator();MatrixIterator<T> diagonalIterator();
}// 具体矩阵实现
public class ArrayMatrix<T> implements Matrix<T> {private final T[][] data;public ArrayMatrix(T[][] data) {this.data = data;}@Overridepublic int getRows() {return data.length;}@Overridepublic int getColumns() {return data[0].length;}@Overridepublic T get(int row, int col) {return data[row][col];}// 行优先迭代器@Overridepublic MatrixIterator<T> rowMajorIterator() {return new RowMajorIterator();}// 列优先迭代器@Overridepublic MatrixIterator<T> columnMajorIterator() {return new ColumnMajorIterator();}// 对角线迭代器@Overridepublic MatrixIterator<T> diagonalIterator() {return new DiagonalIterator();}// 行优先迭代器实现private class RowMajorIterator implements MatrixIterator<T> {private int row = 0;private int col = 0;@Overridepublic boolean hasNext() {return row < getRows() && col < getColumns();}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();T element = get(row, col);col++;if (col >= getColumns()) {col = 0;row++;}return element;}}// 列优先迭代器实现private class ColumnMajorIterator implements MatrixIterator<T> {private int row = 0;private int col = 0;@Overridepublic boolean hasNext() {return col < getColumns() && row < getRows();}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();T element = get(row, col);row++;if (row >= getRows()) {row = 0;col++;}return element;}}// 对角线迭代器实现private class DiagonalIterator implements MatrixIterator<T> {private int diag = 0;private int pos = 0;@Overridepublic boolean hasNext() {return diag < (getRows() + getColumns() - 1);}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();int row, col;if (diag < getRows()) {row = diag - pos;col = pos;} else {row = getRows() - 1 - pos;col = diag - getRows() + 1 + pos;}T element = get(row, col);pos++;if (pos > diag || (diag >= getRows() && pos >= getRows() + getColumns() - diag - 1)) {diag++;pos = 0;}return element;}}
}

3.3 客户端使用

public class MatrixClient {public static void main(String[] args) {Integer[][] matrixData = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};Matrix<Integer> matrix = new ArrayMatrix<>(matrixData);System.out.println("行优先遍历:");printIterator(matrix.rowMajorIterator());System.out.println("\n列优先遍历:");printIterator(matrix.columnMajorIterator());System.out.println("\n对角线遍历:");printIterator(matrix.diagonalIterator());}private static void printIterator(MatrixIterator<?> iterator) {while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();}
}/* 输出:
行优先遍历:
1 2 3 4 5 6 7 8 9 列优先遍历:
1 4 7 2 5 8 3 6 9 对角线遍历:
1 2 4 3 5 7 6 8 9 
*/

3.4 遍历过程可视化

1-2-3-4-5-6-7-8-9
1-4-7-2-5-8-3-6-9
1-2-4-3-5-7-6-8-9
行优先遍历
顺序访问
列优先遍历
跨行访问
对角线遍历
斜向访问

四、迭代器模式进阶技巧

4.1 线程安全迭代器

public class SafeIterator<T> implements Iterator<T> {private final Object lock = new Object();private final Iterator<T> delegate;public SafeIterator(Iterator<T> delegate) {this.delegate = delegate;}@Overridepublic boolean hasNext() {synchronized (lock) {return delegate.hasNext();}}@Overridepublic T next() {synchronized (lock) {return delegate.next();}}@Overridepublic void remove() {synchronized (lock) {delegate.remove();}}
}

4.2 过滤迭代器

public class FilterIterator<T> implements Iterator<T> {private final Iterator<T> source;private final Predicate<T> filter;private T nextElement;private boolean hasNext;public FilterIterator(Iterator<T> source, Predicate<T> filter) {this.source = source;this.filter = filter;findNext();}private void findNext() {while (source.hasNext()) {T candidate = source.next();if (filter.test(candidate)) {nextElement = candidate;hasNext = true;return;}}hasNext = false;}@Overridepublic boolean hasNext() {return hasNext;}@Overridepublic T next() {if (!hasNext) throw new NoSuchElementException();T result = nextElement;findNext();return result;}
}// 使用示例
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Iterator<Integer> evenIterator = new FilterIterator<>(numbers.iterator(), n -> n % 2 == 0
);

4.3 分页迭代器

public class PagingIterator<T> implements Iterator<List<T>> {private final Iterator<T> source;private final int pageSize;public PagingIterator(Iterator<T> source, int pageSize) {this.source = source;this.pageSize = pageSize;}@Overridepublic boolean hasNext() {return source.hasNext();}@Overridepublic List<T> next() {if (!hasNext()) throw new NoSuchElementException();List<T> page = new ArrayList<>(pageSize);for (int i = 0; i < pageSize && source.hasNext(); i++) {page.add(source.next());}return page;}
}// 使用示例
List<Integer> bigList = IntStream.range(0, 1000).boxed().collect(Collectors.toList());
Iterator<List<Integer>> pageIterator = new PagingIterator<>(bigList.iterator(), 100);while (pageIterator.hasNext()) {List<Integer> page = pageIterator.next();processPage(page);
}

五、应用场景分析

5.1 典型应用场景

场景迭代器应用优势
集合框架Java集合遍历统一访问接口
文件系统目录遍历隐藏文件系统差异
数据库结果集遍历抽象数据库访问
树形结构多种遍历算法解耦结构与遍历
流处理数据管道支持链式操作

5.2 使用时机判断

当出现以下情况时考虑迭代器模式:

  1. 需要统一访问不同聚合结构
  2. 需要支持多种遍历方式
  3. 需要隐藏聚合的内部结构
  4. 需要为聚合提供遍历接口

5.3 不适用场景

  1. 简单数组或列表遍历
  2. 性能极度敏感的场景
  3. 只需要顺序访问的不可变集合

六、模式优劣辩证

6.1 优势 ✅

35% 25% 20% 15% 5% 迭代器模式优势 解耦集合与遍历 多种遍历支持 统一访问接口 延迟加载支持 开闭原则支持

6.2 劣势 ❌

  1. 性能开销:比直接索引访问慢
  2. 复杂性增加:简单场景过度设计
  3. 并发修改问题:迭代中修改集合导致异常
  4. 单向遍历限制:标准迭代器只支持单向移动

七、与其他模式的关系

7.1 迭代器模式 vs 访问者模式

遍历元素
对元素执行操作
迭代器模式
访问者模式
  • 协同关系:迭代器负责遍历,访问者负责操作
  • 区别
    • 迭代器:关注元素访问顺序
    • 访问者:关注元素操作逻辑

7.2 迭代器模式 vs 组合模式

维度迭代器模式组合模式
目的遍历元素构建树形结构
关注点访问顺序结构组织
典型配合常遍历组合结构常提供迭代器

八、在Java集合框架中的应用

8.1 迭代器接口演进

Enumeration
Iterator
ListIterator
Spliterator

8.2 ArrayList迭代器实现

// ArrayList内部迭代器
private class Itr implements Iterator<E> {int cursor;       // 下一个元素索引int lastRet = -1; // 最后返回的元素索引int expectedModCount = modCount; // 并发修改检查public boolean hasNext() {return cursor != size;}public E next() {checkForComodification();int i = cursor;Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.remove(lastRet);cursor = lastRet;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}

8.3 Java 8 Spliterator

public interface Spliterator<T> {boolean tryAdvance(Consumer<? super T> action);Spliterator<T> trySplit();long estimateSize();int characteristics();
}// 使用示例
List<String> list = Arrays.asList("a", "b", "c");
Spliterator<String> spliterator = list.spliterator();// 顺序处理
spliterator.forEachRemaining(System.out::println);// 并行处理
spliterator.trySplit().forEachRemaining(System.out::println);

九、最佳实践指南

9.1 设计建议

  1. 使用Iterator而不是直接访问

    // 好
    for (Iterator<Item> it = collection.iterator(); it.hasNext(); ) {Item item = it.next();// ...
    }// 更好 (Java 5+)
    for (Item item : collection) {// ...
    }
    
  2. 支持fail-fast机制

    public class SafeIterable<T> implements Iterable<T> {private final List<T> data = new ArrayList<>();private volatile int modCount = 0;@Overridepublic Iterator<T> iterator() {return new SafeIterator<>(data.iterator(), modCount);}private class SafeIterator implements Iterator<T> {private final Iterator<T> delegate;private final int expectedModCount;public SafeIterator(Iterator<T> delegate, int modCount) {this.delegate = delegate;this.expectedModCount = modCount;}private void checkModification() {if (modCount != expectedModCount) {throw new ConcurrentModificationException();}}@Overridepublic boolean hasNext() {checkModification();return delegate.hasNext();}// next() 和 remove() 类似}
    }
    

9.2 性能优化

  1. 随机访问优化

    public class RandomAccessIterator<T> implements Iterator<T> {private final RandomAccess randomAccess;private final int size;private int index = 0;public RandomAccessIterator(List<T> list) {if (!(list instanceof RandomAccess)) {throw new IllegalArgumentException("List must support random access");}this.randomAccess = (RandomAccess) list;this.size = list.size();}@Overridepublic boolean hasNext() {return index < size;}@Overridepublic T next() {return ((List<T>) randomAccess).get(index++);}
    }
    
  2. 延迟加载迭代器

    public class LazyIterator<T> implements Iterator<T> {private final Supplier<T> supplier;private T next;public LazyIterator(Supplier<T> supplier) {this.supplier = supplier;advance();}private void advance() {next = supplier.get();}@Overridepublic boolean hasNext() {return next != null;}@Overridepublic T next() {if (next == null) throw new NoSuchElementException();T result = next;advance();return result;}
    }
    

9.3 遍历策略对比

策略时间复杂度空间复杂度适用场景
顺序迭代O(n)O(1)链表、文件流
随机访问O(1)O(1)数组、ArrayList
分页迭代O(n)O(pageSize)大数据集
并行迭代O(n/p)多核处理器

十、总结:迭代器模式的核心价值

迭代器模式通过解耦遍历实现了:

独立演化
独立演化
简化客户端
集合实现
系统灵活性
遍历算法
统一接口
代码可维护性

设计启示
将遍历视为独立于集合结构的操作,让集合专注存储,迭代器专注访问

正如《设计模式》作者GoF所强调:

“迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示”


扩展思考

  1. 如何在分布式系统中实现迭代器?
  2. 迭代器模式如何与响应式编程结合?
  3. 如何设计支持双向遍历的迭代器?

附录:Java迭代器发展史

版本特性代表类/接口
JDK 1.0基本枚举Enumeration
JDK 1.2标准迭代器Iterator
JDK 1.5增强for循环Iterable
JDK 1.5双向迭代ListIterator
JDK 8并行迭代Spliterator
JDK 8流式迭代Stream

迭代器模式是构建灵活、可扩展集合框架的基石,其设计思想深刻影响了现代编程语言的数据处理范式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85349.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Security OAuth2 组件

我们来系统地讲解一下 Spring Security OAuth2 这个强大的组件。我会从概念、作用、核心组件&#xff0c;以及实际应用场景来为你剖析。 1. 什么是 Spring Security OAuth2&#xff1f; 简单来说&#xff0c;Spring Security OAuth2 是 Spring Security 框架的一个模块&#…

Redis的持久化功能

Redis的持久化功能能够将内存中的数据保存到磁盘&#xff0c;从而在重启后恢复数据。下面为你详细介绍Redis的两种主要持久化方式及其配置方法。 RDB&#xff08;Redis Database&#xff09;持久化 RDB持久化是通过生成某个时间点的数据集快照来实现的。它具有高性能的特点&a…

Chrome 将成为下一个 IE6

最近在技术圈刷到一个帖子&#xff0c;说&#xff1a;“Chrome 就快变成新的 IE6 了。” 乍一看有点危言耸听&#xff0c;但你一细品&#xff0c;发现还真挺像回事。 想当年&#xff1a;IE6 是怎么垮的&#xff1f; IE6 当年多风光&#xff1f;全球市场份额一度超过 90%&#…

Redis 配置文件详解redis.conf 从入门到实战

一、redis.conf 是什么&#xff1f; Redis 的配置文件&#xff08;默认命名为 redis.conf&#xff0c;Redis 8.0 之后改为 redis-full.conf&#xff09;控制着服务运行的各项参数。该文件采用以下结构&#xff1a; 指令名 参数1 参数2 ... 参数N例如&#xff1a; replicaof …

autoware docker的安装

前言 官方的安装说明&#xff1a; 官方的安装说明 安装前&#xff0c;请确认安装的硬件&#xff1a; CPU with 8 cores16GB RAM[Optional] NVIDIA GPU (4GB RAM) 满足需求 1. 安装软件依赖 这一步主要是安装三个软件&#xff1a; DockerNVIDIA Container Toolkit (pref…

AWS 解决方案深度剖析:Amazon QLDB — 构建可信赖、不可变的数据审计基石

导言&#xff1a;数据可信的挑战 在现代应用开发中&#xff0c;尤其是在金融、供应链、身份认证、政府事务、医疗记录管理等领域&#xff0c;数据完整性和历史追溯性至关重要。我们常常面临以下挑战&#xff1a; 审计困难&#xff1a; 如何证明数据从诞生至今未被篡改&#xf…

Leetcode-​1358. 包含所有三种字符的子字符串数目​

Problem: 1358. 包含所有三种字符的子字符串数目 思路 滑动窗口 解题过程 滑动窗口&#xff1a;使用左右指针 l 和 r 维护一个窗口&#xff0c;窗口内字符的频次由 cnt 记录。 右指针扩展&#xff1a;右指针 r 不断右移&#xff0c;将字符加入窗口并更新频率。 左指针收缩&a…

iTunes 无法备份 iPhone:10 种解决方法

Apple 设备是移动设备市场上最先进的产品之一&#xff0c;但有些人遇到过 iTunes 因出现错误而无法备份 iPhone 的情况。iTunes 拒绝备份 iPhone 时&#xff0c;可能会令人非常沮丧。不过&#xff0c;幸运的是&#xff0c;我们有 10 种有效的方法可以解决这个问题。您可以按照以…

Unity 接入抖音小游戏一

目录 一、搭建小游戏环境 二、接入抖音SDK 1.初始化 2.登录 3.分享 4.添加到桌面 5.侧边栏功能 6. 接入流量主 三、完整代码 下一篇传送门 Unity 接入抖音小游戏二 -CSDN博客 一、搭建小游戏环境 我这边因为没有下载其他版本的Unity所以就先用2022.3.57f1了 大家还是下载…

Node.js 项目启动命令全面指南:从入门到精通(术语版)

文章目录 Node.js 项目启动命令全面指南&#xff1a;从入门到精通一、核心启动命令深度解析1. 基础命令结构与执行机制2. 参数传递机制详解 二、常用命令分类详解1. 运行环境命令对比2. 质量保障命令详解3. 构建部署全流程 三、高级配置实战技巧1. 环境变量管理进阶2. 命令组合…

创意风格行业PPT模版分享

极简主题PPT模版&#xff0c;设计类PPT模版&#xff0c;快乐童年成长PPT模版&#xff0c;教育机构通用PPT模版&#xff0c;创意风格行业PPT模版 创意风格行业PPT模版分享&#xff1a;https://pan.quark.cn/s/3bac52e09479

Java + Spring Boot + MyBatis 枚举变量传递给XML映射文件做判断

枚举定义 ReagentStatus.java package com.weiyu.utils.enums;import lombok.Getter;/*** 试剂状态枚举*/ Getter public enum ReagentStatus {// 常规REGULAR,// 少库存LESS_INVENTORY,// 零库存ZERO_INVENTORY,// 将过期WILL_EXPIRE,// 已过期EXPIRED,// 已注销LOGGED,// 全…

华为云Flexus+DeepSeek征文 | 华为云CCE容器高可用部署Dify高可用版实测:从0到1的高可靠应用实践

引言 随着大语言模型&#xff08;LLM&#xff09;技术的爆发&#xff0c;如何快速构建具备高可用、弹性扩展能力的AI应用开发平台&#xff0c;成为企业数字化转型的关键命题。华为云依托其云原生基础设施&#xff0c;推出CCE容器高可用版Dify部署方案&#xff0c;通过“一键部…

c++_cout的理解和使用

问题引入 cout << (uf.is_same_set(x, y)) ? Y : N<<endl; 请问大家&#xff0c;这条语句对吗&#xff1f;&#xff08;这里的uf.is_same_set(x, y)是一个自定义函数&#xff0c;返回bool值&#xff1b;所以不是问题的关键&#xff09;》 答案是这条语句报错了…

山东大学项目实训-创新实训-法律文书专家系统-项目报告(八)

项目实训博客 : 项目后端架构 , 项目的四端交互(前端 ,后端 ,模型端 ,数据库)的开发和维护 , 项目功能总览 作为项目的后端和前端交互功能主要开发者,我需要对项目的四端交互进行开发和维护. 总览: 整体项目结构如图所示: 前后端的交互: 前端封装了request.js : 方便前端…

12.8Java Swing 中的MVC

在 Java Swing 中&#xff0c;MVC 模式被广泛应用。例如&#xff0c;JTable、JList 等组件都采用了这种模式。通常&#xff1a; 模型&#xff1a;实现特定的 Swing 模型接口&#xff08;如 TableModel、ListModel&#xff09;。视图&#xff1a;是 Swing 组件本身&#xff08;…

DDS(Data Distribution Service)

DDS&#xff08;Data Distribution Service&#xff09;是一种以数据为中心的发布/订阅&#xff08;DCPS&#xff09;通信中间件协议栈标准&#xff08;由OMG组织维护&#xff09;。它专为高性能、可预测、实时、可靠的分布式系统设计&#xff0c;广泛应用于国防、航空航天、工…

python爬虫关于多进程,多线程,协程的使用

简介&#xff1a; python其实没有真正意义的多线程&#xff0c;因为有GIL锁存在&#xff0c;但是python3.13去掉GIL锁&#xff0c;有两个版本&#xff0c;python3.13t和python3.13&#xff0c;python3.13去掉GIL锁相当于python底层大规模改变&#xff0c;肯定会影响一些库的使…

java 设计模式_行为型_23状态模式

23.状态模式 Java中的状态设计模式是一种软件设计模式&#xff0c;当对象的内部状态更改时&#xff0c;该模式允许对象更改其行为。状态设计模式通常用于以下情况&#xff1a;对象取决于其状态&#xff0c;并且在运行期间必须根据其内部状态更改其行为。状态设计模式是许多行为…

Flink CDC MySQL 时区相差 8 小时问题优雅解决方式

Flink CDC MySQL 时区相差 8 小时问题解析 代码运行环境 Flink 1.15 + FlinkCDC 2.4.0 + jdk1.8 +springboot 2.31、原因分析 Flink CDC 底层使用 Debezium 连接器来捕获 MySQL 的数据变更,而 Debezium 在解析 MySQL 的 binlog 日志时,默认使用 UTC 时区来处理时间字段。若…