设计模式-迭代器模式

作者:紫菜紫薯紫甘蓝日期:2025/10/13

设计模式-迭代器模式

迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern),用来给类实例提供一种遍历对象的方式。

案例分析

首先写一个经典的 User 类

1@Data
2@ToString
3public class User {
4
5    private String uuid;
6
7    private String name;
8
9    private Integer age;
10
11}
12

通常我们遍历一个对象有三种方式

1public class Main {
2    public static void main(String[] args) {
3
4        List<User> userList = IntStream.rangeClosed(1, 10).mapToObj(i -> {
5            User user = new User();
6            user.setUuid(i + "");
7            user.setAge(i);
8            user.setName("name" + i);
9            return user;
10        }).collect(Collectors.toList());
11
12        for (int i = 0; i < userList.size(); i++) {
13            User temp = userList.get(i);
14            System.out.println(temp);
15        }
16
17        for (User temp : userList) {
18            System.out.println(temp);
19        }
20
21        Iterator<User> iterator = userList.iterator();
22        while (iterator.hasNext()) {
23            User temp = iterator.next();
24            System.out.println(temp);
25        }
26
27    }
28}
29

不过如果分析上述这段代码编译出的字节码文件,其实方法二和方法三最终的实现是一样的

1        for (User temp : userList) {
2            System.out.println(temp);
3        }
4
5        Iterator<User> iterator = userList.iterator();
6        while (iterator.hasNext()) {
7            User temp = iterator.next();
8            System.out.println(temp);
9        }
10

实现一个迭代器其实并不复杂,我们可能会好奇一个问题,为什么 userList 可以放在增强 for 循环的位置

1        for (User temp : userList) {
2            System.out.println(temp);
3        }
4

而 User 对象本身不能放在增强 for 循环的位置

1        for (String temp : User) {
2            System.out.println(temp);
3        }
4

结合标题迭代器模式我们很容易想到这个因为 userList 对应的 ArrayList 类实现了迭代器接口

1public interface Iterable<T> {
2    /**
3     * Returns an iterator over elements of type {@code T}.
4     *
5     * @return an Iterator.
6     */
7    Iterator<T> iterator();
8}    
9

所以如果想让 User 类也可以放到增强 for 循环的位置,也需要实现这个接口

1public class User implements Iterable<String>{
2
3    private String uuid;
4
5    private String name;
6
7    private Integer age;
8
9    @Override
10    public Iterator<String> iterator() {
11        return new UserIte();
12    }
13    
14    private class UserIte implements Iterator<String> {
15
16        @Override
17        public boolean hasNext() {
18            return false;
19        }
20
21        @Override
22        public String next() {
23            return null;
24        }
25    }
26    
27}
28

所以实现一个迭代器类只需要两步:

  • 类实现 Iterable,代表是可迭代的
  • 在类中创建一个内部类实现迭代器接口,实现其中的是否还有下一个元素接口和获取下一个元素接口

由于我们需要记录是否遍历完成,所以 UserIte 中需要使用属性记录当前遍历到的位置

1@Data
2@ToString
3public class User implements Iterable<String> {
4
5    private String uuid;
6
7    private String name;
8
9    private Integer age;
10
11    @Override
12    public Iterator<String> iterator() {
13        return new UserIte();
14    }
15
16    private class UserIte implements Iterator<String> {
17
18        private Integer index;
19
20        private List<String> propertyList = new ArrayList<>();
21
22        public UserIte() {
23            index = 0;
24            propertyList.add(User.this.uuid);
25            propertyList.add(User.this.name);
26            propertyList.add(User.this.age + "");
27        }
28
29        @Override
30        public boolean hasNext() {
31            return index < propertyList.size();
32        }
33
34        @Override
35        public String next() {
36            if (index >= propertyList.size()) {
37                throw new NoSuchElementException("没有" + index + "对应的元素");
38            }
39            return propertyList.get(index++);
40        }
41    }
42
43}
44

这样就可以对 User 的对象进行遍历了

1public class Main {
2    public static void main(String[] args) {
3        List<User> userList = IntStream.rangeClosed(1, 10).mapToObj(i -> {
4            User user = new User();
5            user.setUuid(i + "");
6            user.setAge(i);
7            user.setName("name" + i);
8            return user;
9        }).collect(Collectors.toList());
10
11        User temp = userList.get(0);
12        for (String s : temp) {
13            System.out.println(s);
14        }
15    }
16}
17

实现一个迭代器并不是很复杂,但是如果你仔细看上面的实现就会有疑问,如果在遍历的过程中对元素进行了变化,那么迭代过程中会出现什么问题呢?

这里不再演示之间说结论,有可能出错也有可能不出错,迭代器中有一个 cursor 的概念,Mysql 中其实也有这个游标的概念。

image-20251012191425312

如上图所示:当游标指向 b 的时候,删除了数组中的 a 元素,由于数组会进行补齐,所以 cursor 会指向 d,元素 c 就会错过遍历。如果指向最后一个元素的时候被删了,那么可能会出现空指针。事实上,在计算机中,不可预知的结果(出错或不出错)比出错更让人头疼,因此最好的处理方式就是只要出现了预期之外的操作就报错,让开发人员尽快处理这样的操作。

如果你查看 ArrayList 的实现,会发现其在每次进行 add 或 remove 操作的时候,会对其中的 modCount 进行 +1,记录出现的更新次数,并在创建迭代器时将该次数传入,并在每次进行遍历时先判断迭代器中修改次数的和实例的修改次数是否相同,如果不同就抛出异常。

总结

所以,实现一个迭代器模式并不复杂,但是迭代器存在的意义是什么呢?为什么不都用 for i 类型的遍历呢?

对于复杂的数据结构(比如树、图)来说,有各种复杂的遍历方式。比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等。如果由客户端代码来实现这些遍历算法,势必增加开发成本,而且容易写错。如果将这部分遍历的逻辑写到容器类中,也会导致容器类代码的复杂性。

前面也多次提到,应对复杂性的方法就是拆分。我们可以将遍历操作拆分到迭代器类中。比如,针对图的遍历,我们就可以定义 DFSIterator、BFSIterator 两个迭代器类,让它们分别来实现深度优先遍历和广度优先遍历。

其次,将游标指向的当前位置等信息,存储在迭代器类中,每个迭代器独享游标信息。这样,我们就可以创建多个不同的迭代器,同时对同一个容器进行遍历而互不影响。而且迭代器之间都实现了迭代器接口,可以利用多态特性很方便的互相替换,例如从前遍历转为从后遍历,体现了面向接口编程的思想。


设计模式-迭代器模式》 是转载文章,点击查看原文


相关推荐


医疗设备控制系统中同步与异步通信的架构设计
oioihoii2025/10/11

在医疗设备控制系统的开发过程中,我们面临一个经典的技术挑战:如何在保持用户界面流畅响应的同时,可靠地处理设备控制的长时间操作。本文将通过一个医疗床控制系统的实际案例,分享我们在同步与异步通信架构设计上的解决方案。 问题场景 我们的医疗床控制系统采用主从架构:Host(主控端)与EPC(设备控制单元)通过双端口通信: Command端口:用于发送控制命令和接收立即响应 Event端口:用于接收异步的执行结果和状态更新 关键需求: 用户点击"移动病床"按钮后,需要等待设备执行完成(可能耗时数十


【SCI一区】【电动车】基于ADMM双层凸优化的燃料电池混合动力汽车研究(Matlab代码实现)
荔枝科研社2025/10/9

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭:行百里者,半于九十。 💥1 概述 基于ADMM双层凸优化的燃料电池混合动力汽车研究 随着车辆互联性的出现,互联汽车 (CVs) 在增强道路安全、改善乘坐舒适性、提高交通效率和提高能源效率方面提供了巨大的潜力。通过从车对车 (V2V) 和车对基础设施 (V2I) 通信中获取交通信息,CV 能够更准确、更广泛地感知


第7章:数据库与持久化存储
芝麻开门-新起点2025/10/8

7.1 为何需要数据库:记忆与状态管理 内容讲解 默认情况下,AI Bot 是**“无状态”的。这意味着除了短暂的当前对话上下文,它不记得任何过去的事情。每次对话都是一次全新的开始。然而,在许多真实场景中,我们需要 Bot 拥有记忆**,能够持久化地存储和检索信息。这就是**数据库(Database)**的作用。 数据库为 Bot 提供了以下关键能力: 长期记忆:记住用户的偏好、历史订单、个人信息等。例如,一个订餐 Bot 应该记住你常去的地址和喜欢的口味。状态跟踪:在复杂的多轮任务中,跟踪当前


Python 的 TCP 编程
hubenchang05152025/10/6

#Python 的 TCP 编程 传输控制协议(Transmission Control Protocol) 是一种 面向连接、可靠传输 的网络通信协议,是现代互联网最核心的协议之一。 #客户端程序 TCP 客户端程序通常只需要连接到服务器然后收发数据即可。下面是一个示例,它向 tcpbin.com 的 4242 端口发送 hello\n,对方会原样返回。 import socket # 创建 TCP socket sock = socket.socket(socket.AF_INET, so


【深度相机术语与概念】
是刘彦宏吖2025/10/5

获取相机输出的 深度图、灰度图、彩色图 和 点云图,用于导航、避障、三维建模、手势识别等应用。 【深度相机术语与概念】 相机类型 3D 相机 3D 相机是一种能够捕捉三维图像的相机。它通过各种技术手段(如立体视觉、飞行时间、结构光等)获取物体的三维形状和深度信息。3D 相机可以生成具有 3D 空间坐标信息的点云数据,使得计算机能够理解和处理三维空间中的物体。 主动双目立体相机 主动双目立体相机是一种结合了双目立体视觉和主动光源(如结构光)的相机系统。它通过投射已知的光图案到场景中,并使用双目相


HTTP为什么不安全?
你的人类朋友2025/10/4

🌐 前言 你好呀,我是你的人类朋友! 本文主要讲讲 HTTP 为什么不安全,以及 HTTPS 如何解决这些问题。 ❗❗ 核心问题速览 HTTP(超文本传输协议):互联网上应用最广泛的网络协议,但数据以明文形式传输。注意,是明文,谁都能看!! HTTPS(安全超文本传输协议):HTTP 的安全版本,= HTTP + SSL/TLS 加密,就像把明信片放进防拆信封里寄送,别人无法看到信息的内容。 补充知识 1:SSL/TLS在【传输层】对 HTTP 数据进行加密,确保隐私和完整性。 补充知识 2


SIMD编程入门:让性能飞起来的实践指南
oioihoii2025/10/3

在现代计算中,单指令多数据流(SIMD)技术就像是一把性能优化的瑞士军刀,能让你的程序速度提升数倍甚至数十倍。本文将带你从零开始,掌握这把利器的使用之道。 什么是SIMD?从汽车生产线说起 想象一下汽车生产线:传统方式是一个工人依次安装每个轮胎,而SIMD就像是培训了一个专门团队,能够同时安装四个轮胎。这就是单指令多数据流的核心思想——一条指令,多个数据。 // 传统标量计算 - 依次处理每个元素 for (int i = 0; i < 4; i++) { result[i] = a[


释放模型潜能:ONNX Runtime 如何进行优化与加速?
Cosolar2025/10/2

在机器学习从实验室走向真实世界的过程中,模型的部署与运行效率往往是决定项目成败的“最后一公里”。一个在离线环境中表现优异的模型,如果无法满足生产环境对低延迟、高吞吐和低资源消耗的要求,其商业价值将大打折扣。 ONNX Runtime (ORT) 作为由微软主导的开源跨平台推理引擎,凭借其出色的性能、广泛的硬件支持和活跃的社区,已成为业界部署模型的事实标准之一。然而,仅仅将模型转换为 ONNX 格式并使用 ORT 运行,只是拿到了“入场券”。要真正释放其潜能,我们需要从模型优化、推理引擎配置、硬


AR/VR赋能工业巡检:开启智能化运维新时代
Teamhelper_AR2025/10/2

在工业 4.0 时代浪潮的推动下,增强现实(AR www.teamhelper.cn )与虚拟现实(VR)技术加速从理论概念迈向工业应用前沿,尤其在工业设备巡检这一关键领域,正展现出前所未有的变革潜力,有望彻底颠覆传统依赖人工经验、效率低下、风险高且数据不连贯的巡检模式。 AR技术:重塑工业巡检核心优势 AR技术通过巧妙地将虚拟信息与真实环境相融合,为工业巡检人员带来了革新性的工作体验。借助AR智能眼镜,巡检人员能够实时获取设备参数、操作指南以及历史数据等关键信息,无需再频繁翻阅纸质


《WebAssembly指南》第六章:读懂 WebAssembly 文本格式
锋通科技10/2/2025

本文介绍了WebAssembly文本格式的基本概念和使用方法。主要内容包括:1. WebAssembly文本格式采用S表达式表示模块结构,比二进制格式更易读易修改。2. 详细讲解了函数定义、参数传递、栈机器运行机制等核心概念,并通过加法函数示例演示了模块的创建和调用过程。3. 介绍了内存管理机制,包括内存共享、字符串处理和多内存使用场景。4. 深入讲解了表格(Table)的概念及其在动态链接中的应用,展示了如何通过表格实现函数指针功能。5. 概述了WebAssembly支持的各种类型和特性,包括数值类型、引

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0