设计模式-迭代器模式(Iterator)

1. 概念

  • 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素。
  • 迭代器模式的核心思想是将遍历集合的责任封装到一个单独的对象中,这样可以避免暴露集合内部的表示方式。这种模式通常用于提供一种方法来访问一个容器对象中各个元素,同时又不暴露该对象的内部细节。

2. 原理结构图

在这里插入图片描述

  1. 迭代器(Iterator)角色
    • 定义了一个访问聚合对象元素的接口,它通常包含以下方法:next()(返回下一个元素)、hasNext()(检查是否还有更多元素)、remove()(从聚合对象中移除迭代器最后返回的元素)。
    • 迭代器接口为遍历聚合对象中的元素提供了统一的方式,使得聚合对象的具体实现与遍历方式解耦。
  2. 具体迭代器(Concrete Iterator)角色
    • 实现迭代器接口,完成对聚合对象的遍历。
    • 具体迭代器持有对聚合对象的引用,并通过该引用访问聚合对象中的元素。
    • 具体迭代器知道如何遍历聚合对象的元素,例如通过索引、指针或其他内部表示。
  3. 聚合对象(Aggregate)角色
    • 定义创建迭代器对象的接口,通常是一个返回迭代器对象的方法。
    • 聚合对象持有多个元素,并提供对元素的访问。
    • 聚合对象的具体实现可以是数组、列表、集合等任何可遍历的数据结构。
  4. 具体聚合对象(Concrete Aggregate)角色
    • 实现聚合对象的接口,返回具体迭代器对象。
    • 具体聚合对象维护了元素的集合,并提供给迭代器使用。
    • 当请求迭代器时,具体聚合对象会创建一个具体迭代器实例,该实例将用于遍历聚合对象中的元素。

3. 代码示例

3.1 示例1-导航系统
  • 实现导航系统遍历功能的示例
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;// 定义位置(Location)接口和具体的实现类
// 位置接口
interface Location {String getName();Iterator<Location> getLocations(); // 获取子位置的迭代器
}// 地区类
class Region implements Location {private String name;private List<Location> locations;public Region(String name) {this.name = name;this.locations = new ArrayList<>();}@Overridepublic String getName() {return name;}public void addLocation(Location location) {locations.add(location);}@Overridepublic Iterator<Location> getLocations() {return new LocationIterator(locations);}
}// 城市类
class City implements Location {private String name;private List<Location> attractions; // 假设城市下直接包含景点public City(String name) {this.name = name;this.attractions = new ArrayList<>();}@Overridepublic String getName() {return name;}public void addAttraction(Location attraction) {attractions.add(attraction);}@Overridepublic Iterator<Location> getLocations() {return new LocationIterator(attractions);}
}// 实现迭代器接口和具体的迭代器类
// 迭代器接口
interface Iterator<T> {boolean hasNext();T next();
}// 位置迭代器实现类
class LocationIterator implements Iterator<Location> {private List<Location> locations;private int currentIndex = 0;public LocationIterator(List<Location> locations) {this.locations = locations;}@Overridepublic boolean hasNext() {return currentIndex < locations.size();}@Overridepublic Location next() {if (hasNext()) {return locations.get(currentIndex++);} else {throw new NoSuchElementException();}}
}// 空迭代器实现类,用于景点等没有子位置的情况
class EmptyIterator<T> implements Iterator<T> {@Overridepublic boolean hasNext() {return false;}@Overridepublic T next() {throw new NoSuchElementException();}
}// 景点类
class Attraction implements Location {private String name;public Attraction(String name) {this.name = name;}@Overridepublic String getName() {return name;}@Overridepublic Iterator<Location> getLocations() {// 景点没有子位置,返回空的迭代器return new EmptyIterator<>();}
}public class NavigationSystemDemo {public static void main(String[] args) {// 创建地区、城市和景点对象,并构建层次结构Region region = new Region("华北地区");City cityA = new City("北京");Attraction attraction1 = new Attraction("故宫");Attraction attraction2 = new Attraction("天安门");cityA.addAttraction(attraction1);cityA.addAttraction(attraction2);region.addLocation(cityA);// 使用迭代器遍历地区下的所有景点Iterator<Location> iterator = region.getLocations();while (iterator.hasNext()) {Location location = iterator.next();if (location instanceof City) { // 假设只对城市下的景点感兴趣Iterator<Location> attractionsIterator = location.getLocations();while (attractionsIterator.hasNext()) {Location attraction = attractionsIterator.next();System.out.println(attraction.getName());}}}}
}
  • 将看到如下输出:
故宫
天安门
  • 在这个示例中,创建了一个Region对象表示地区,并在其中添加了一个City对象表示城市。城市下面添加了两个Attraction对象表示景点。Location接口定义了位置的基本行为,包括获取名称和获取子位置的迭代器。LocationIterator类实现了迭代器接口,用于遍历位置的子元素。
  • NavigationSystemDemomain方法中,创建了一个地区对象,并向其中添加了一个城市对象和两个景点对象。然后,使用迭代器来遍历地区下的所有位置。由于只对城市下的景点感兴趣,所以在遍历到城市时,进一步获取城市的迭代器,并遍历其下的景点,输出景点的名称。
  • 需要注意的是,在这个例子中,假设城市下直接包含景点,而没有再进一步的层级结构。如果实际场景中城市下还有更复杂的层级结构(如区域、街道等),那么需要相应地调整City类和迭代器的实现,以支持更深层次的遍历。

3.2 示例2-网络爬虫
  • 实现一个简单的网络爬虫,用于遍历和获取一个网站的所有链接
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;// 定义Link类来表示链接
class Link {private String url;private String title;public Link(String url, String title) {this.url = url;this.title = title;}public String getUrl() {return url;}public String getTitle() {return title;}@Overridepublic String toString() {return "Link{" +"url='" + url + '\'' +", title='" + title + '\'' +'}';}
}// 实现一个WebPage类,表示网页以及该网页包含的链接列表
class WebPage {private String url;private List<Link> links;public WebPage(String url) {this.url = url;this.links = new ArrayList<>();}public void addLink(Link link) {links.add(link);}public List<Link> getLinks() {return links;}public String getUrl() {return url;}
}// 定义迭代器接口LinkIterator和它的实现类WebPageLinkIterator
interface LinkIterator extends Iterator<Link> {// 迭代器接口保持不变
}class WebPageLinkIterator implements LinkIterator {private Iterator<Link> linkIterator;public WebPageLinkIterator(List<Link> links) {this.linkIterator = links.iterator();}@Overridepublic boolean hasNext() {return linkIterator.hasNext();}@Overridepublic Link next() {return linkIterator.next();}@Overridepublic void remove() {linkIterator.remove();}
}// 实现WebCrawler类,它将使用迭代器来遍历网页链接
class WebCrawler {private List<WebPage> visitedPages = new ArrayList<>();public void crawl(String startUrl) {WebPage currentPage = fetchPage(startUrl);if (currentPage != null) {visitedPages.add(currentPage);crawlLinks(currentPage.getLinks());}}private void crawlLinks(List<Link> links) {for (Link link : links) {WebPage linkedPage = fetchPage(link.getUrl());if (!visitedPages.contains(linkedPage)) {visitedPages.add(linkedPage);}}}private WebPage fetchPage(String url) {// 这里应该是网络请求和页面解析的代码,但为了简化,我们直接返回硬编码的页面WebPage page = new WebPage(url);// 假设每个页面都有一些链接page.addLink(new Link("http://example.com/link1", "Link 1"));page.addLink(new Link("http://example.com/link2", "Link 2"));// 添加更多链接...return page;}public void printVisitedLinks() {for (WebPage page : visitedPages) {LinkIterator iterator = new WebPageLinkIterator(page.getLinks());while (iterator.hasNext()) {Link link = iterator.next();System.out.println("Visited link: " + link);}}}
}public class CrawlerDemo {public static void main(String[] args) {WebCrawler crawler = new WebCrawler();String startUrl = "http://example.com";crawler.crawl(startUrl);crawler.printVisitedLinks();}
}
  • 将看到如下输出:
Visited link: Link{url='http://example.com/link1', title='Link 1'}
Visited link: Link{url='http://example.com/link2', title='Link 2'}
Visited link: Link{url='http://example.com/link1', title='Link 1'}
Visited link: Link{url='http://example.com/link2', title='Link 2'}
Visited link: Link{url='http://example.com/link1', title='Link 1'}
Visited link: Link{url='http://example.com/link2', title='Link 2'}
  • 上面的CrawlerDemo类中,创建了一个WebCrawler实例,并调用crawl方法开始从指定的起始URL进行网络爬虫。之后,我们调用printVisitedLinks方法来打印所有访问过的链接。

4. 优缺点

  • 主要作用
    • 将集合的遍历行为从集合对象中分离出来,使得客户端可以一致的方式访问不同类型的集合。
  • 优点
    • 简化遍历:通过提供统一的遍历接口,简化了对聚合对象的访问过程。
    • 解耦遍历逻辑:将遍历逻辑与聚合对象的内部表示分离,降低耦合度。
    • 多态遍历:支持多种遍历方式,提高灵活性。
    • 扩展性:可以方便地添加新的遍历方式,无需修改聚合对象。
    • 代码复用:迭代器可复用,减少重复代码。
  • 缺点
    • 系统复杂性增加:每当新增一个集合类,就需要增加相应的迭代器类,这会导致类的个数成对增加,增加了系统的复杂性。
    • 实现成本提高:由于需要为每个集合类配备对应的迭代器,这可能会增加实现的成本和工作量。
    • 维护难度加大:随着类的数量增加,维护和管理这些类的难度也随之增大。
    • 错误使用风险:如果不正确使用迭代器(如忘记关闭或未完全遍历),可能导致资源泄漏或数据不一致。

5. 应用场景

5.1 主要包括以下几个方面
  1. 访问复杂数据结构:当需要遍历的数据结构较为复杂时,如栈、树、图等,迭代器模式可以提供一种统一的方式来访问这些结构中的元素,而不需要暴露其内部实现细节。
  2. 支持多种遍历方式:如果一个集合需要支持多种遍历方式,例如深度优先或广度优先遍历,迭代器模式可以轻松地为每种遍历方式提供一个具体的迭代器实现。
  3. 扩展性和维护性:当系统需要添加新的集合类型或者修改现有集合类型的遍历方式时,迭代器模式可以简化这一过程,因为新增或修改的代码主要集中在迭代器类中,而不是集合类本身。
  4. 解耦客户端和集合类:迭代器模式通过抽象的迭代器接口,使得客户端代码与集合类的实现细节解耦,这样即使集合的内部结构发生变化,也不会影响到客户端代码。
  5. 统一的元素访问方式:不同的集合可能有不同的内部结构和遍历方式,迭代器模式确保了客户端可以通过一致的接口来访问集合中的元素,提高了代码的可读性和可维护性。

5.2 实际应用
  1. 导航系统:在地图或导航系统中,可以使用迭代器模式来遍历不同的路线或路径,以便找到最佳的导航方案。
  2. 游戏开发:在游戏中,可以使用迭代器模式来遍历游戏世界中的不同元素,如角色、道具、敌人等。
  3. 数据处理:在处理大量数据时,如数据库记录、文件内容等,可以使用迭代器模式来逐行或逐个元素地读取和处理数据。
  4. 网络爬虫:在网页链接的遍历中,迭代器模式使得爬虫能够专注于链接处理,而非其内部实现。

6. JDK中的使用

  • Java集合框架
    • 迭代器接口
      • 在JDK中,迭代器模式的核心是Iterator接口。这个接口定义了遍历集合元素所需的基本方法,包括:
        • hasNext():检查是否还有更多元素可以迭代。
        • next():返回迭代的下一个元素。
        • remove():从底层集合中移除由最后一次调用next()方法返回的元素(可选操作)。
    • 集合类中的迭代器实现
      • Java集合框架中的每个集合类(如ArrayList、HashSet等)都实现了Iterable接口,该接口要求实现一个返回迭代器的方法iterator()。这样,客户端代码就可以通过调用集合对象的iterator()方法来获取一个迭代器,进而遍历集合中的元素。

7. 注意事项

  • 避免外部修改:在迭代过程中,应避免通过外部操作修改被迭代的集合,这可能导致迭代器失效或产生不可预期的结果。
  • 迭代器状态管理:迭代器通常具有内部状态,用于跟踪当前迭代的位置。应确保迭代器的状态管理正确,避免状态混乱或越界访问。
  • 线程安全:在多线程环境下使用迭代器时,需要注意线程安全问题。可能需要采取同步措施来确保迭代过程的正确性和一致性。
  • 空集合处理:对于空集合,迭代器应能够正确处理,避免在迭代过程中抛出异常或产生错误。
  • 接口一致性:确保迭代器的接口设计一致,使得客户端代码可以无缝地切换到不同的迭代器实现,提高代码的可维护性和可扩展性。

8. 迭代器模式 VS 组合模式

模式类型目的模式架构核心角色应用场景
迭代器模式行为型统一的方法来遍历不同类型的集合迭代器接口(Iterator)遍历集合元素的场景
组合模式结构型一致的方式处理个别对象和组合对象组件(Component)和复合对象(Composite)表示对象的部分-整体层次结构

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

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

相关文章

使用 Docker 部署 Draw.io 在线流程图系统

1&#xff09;介绍 Draw.io GitHub&#xff1a;https://github.com/jgraph/drawio Draw.io 是一款开源的绘制流程图的工具&#xff0c;拥有大量免费素材和模板。程序本身支持中文在内的多国语言&#xff0c;创建的文档可以导出到多种网盘或本地。无论是创建流程图、组织结构图…

如何帮助中小企业建立数字化的能力?

中小企业建立数字化的能力&#xff0c;可以从以下几个方面着手&#xff1a; 1、开展数字化评估&#xff1a;中小企业首先需要对自己的数字化基础水平和企业经营管理现状进行评估&#xff0c;这包括了解企业在数字化方面的现有能力和需求&#xff0c;以及内外部转型资源的可用性…

[创业之路-106] :经济学十大陷阱与核心思想:系统论、社会进化论、周期论、阴阳互转论

目录 前言&#xff1a; 一、流动性陷阱。 二、中等收入陷阱。 三、修昔底德陷阱。 四、塔西佗陷阱。 五、金德尔伯格陷阱。 六、卢梭陷阱。 七、拉美陷阱。 八、阿喀琉斯之踵。 九、布拉德伯里悖论。 十、李约瑟之谜 结论&#xff1a;上述陷阱的…

AI智能客服机器人原来这么好用,企业再不使用就落伍了!

随着人工智能技术的不断成熟&#xff0c;AI智能客服机器人已经变得越来越智能&#xff0c;它们正逐渐成为企业提供客户服务的强大助手。企业若不开始部署这种高效的技术&#xff0c;可能会在竞争中失去先机。下面&#xff0c;让我们来看看AI智能客服机器人为何如此好用&#xf…

vue +antvX6 根据节点与线,动态设置节点坐标生成流程图

需求 vue2 + antvX6完成流程图,但只有节点与线,没有节点的坐标,需要根据节点的顺序显示流程图。 需求: 1.根据数据动态生成对应的节点与线; 2.节点不能重叠; 3.节点与线可拖拽; 4.因为线存在重叠可能,所有鼠标移入时线必须高亮显示(红色),鼠标移出复原; 5.要求有…

Spring-基于xml自动装配

版本 Spring Framework 6.0.9​ 1. 定义 Spring IoC容器在无需显式定义每个依赖关系的情况下&#xff0c;根据指定的策略&#xff0c;自动为指定的bean中所依赖的类类型或接口类型属性赋值。 2. 关键配置元素 BeanDefinitionParserDelegate类定义了autowire属性的属性值&…

绿联 安装transmission

绿联 安装transmission及中文UI 1、镜像 linuxserver/transmission:latest 2、安装 2.1、创建容器 按需配置权重。 2.2、基础设置 2.3、网络 桥接即可。 注&#xff1a;如果使用IPV6&#xff0c;请选择"host"模式。 注&#xff1a;如果使用IPV6&#xff0c;请选…

Git禁止松散对象loose objects弹窗

打开仓库时&#xff0c;弹窗如图 This repository currently has approximately XXXX loose objects.解决办法&#xff1a;见How to skip “Loose Object” popup when running ‘git gui’ Git v1.7.9 或以上版本&#xff0c;执行git config --global gui.gcwarning false

【计算机毕业设计】面向学生成绩分析系统产品功能介绍——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

NIO之ByteBuffer

NIO中的ByteBuffer是缓冲区&#xff0c;其中有几个比较重要的属性capacity&#xff0c;position和limit。 capacity&#xff1a; 其中&#xff0c;capacity是缓冲区的容量大小&#xff0c;在分配内存空间后不会改变。 limit&#xff1a; limit是限制位置&#xff0c;在读写模…

前端crypto-js, 文件加密,判断相同文件、图片(MD5,SHA256)

文章目录 前情提要应用场景实战解析最后前情提要 大家好,今天我们来接触一个库crypto-js 没错,上面是有道翻译的截图,为了我们得到的信息更权威,这个库是用来加密的,但介绍是说,已经停止维护,但并不影响我们在前端项目中的使用,所以学学也没有坏处 应用场景 判断图片…

亿发:新修订器械GSP重大变化,医疗器械GSP软件助力适应科学监管

随着医疗器械行业的快速发展和监管要求的提升&#xff0c;国家药监局近期发布了新修订的医疗器械GSP&#xff08;Good Storage Practice&#xff09;规范&#xff0c;旨在加强医疗器械流通环节的质量管理和监督&#xff0c;确保医疗器械的安全、有效和可追溯。这一重大变化为医…

万益蓝Wonderlab益生菌:吃雪糕的自由,我终于找回来了!

虽然现在不是夏天&#xff0c;但是我必须要来说一下&#xff01;我不仅在夏天可以吃雪糕&#xff01;冬天吃也完全没有问题&#xff01; 夏天&#xff0c;对于很多人来说&#xff0c;是雪糕、冷饮和冰镇西瓜的代名词。但对于我&#xff0c;这简直就是个“禁区”。每次看着别人…

SpringBoot项目启动的时候直接退出了?

SpringBoot项目启动的时候直接退出了&#xff1f; 如下图&#xff1a; 在启动SpringBoot项目的时候仅仅用了1.209s&#xff0c;然后直接退出了&#xff0c;也没有出现启动的项目对应的服务器端口号&#xff1f;为什么会这样呢&#xff1f;因为既然有服务器端口号 那么肯定会有…

【php开发工程师系统性教学】——Laravel框架(验证码)的配置和使用的保姆式教程

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

MySQL的事务相关的语句的使用

MySQL的事务相关的语句的使用 事务是数据库管理系统执行过程中的一个程序单位&#xff0c;由一个或多个数据库操作组成。MySQL作为一款流行的关系型数据库管理系统&#xff0c;支持事务处理&#xff0c;允许用户定义一系列的操作&#xff0c;这些操作要么完全执行&#xff0c;…

数仓建模—数仓架构发展史

数仓建模—数仓架构发展史 时代的变迁,生死的轮回,历史长河滔滔,没有什么是永恒的,只有变化才是不变的,技术亦是如此,当你选择互联网的那一刻,你就相当于乘坐了一个滚滚向前的时代列车,开往未知的方向,不论什么样的技术架构只有放在当前的时代背景下,才是有意义的,…

MySQl-8.3.0版本安装下载教程(超详细保姆级教程)

第一步&#xff0c;去百度找到MySQl官网 第二步,找到DOWNLOAD&#xff08;下载&#xff09; 第三步 第四步 第五步 第六步.选择倒数第2个 第七步 第八步然后根据步骤安装就好了

MultiCD工具:创建一个多引导Linux USB驱动器

众所周知&#xff0c;拥有一个可安装多个可用操作系统的 CD 或 USB 驱动器在各种情况下都非常有用。无论是为了快速测试或调试某些内容&#xff0c;还是只是重新安装笔记本电脑或 PC 的操作系统&#xff0c;这都可以为你节省大量时间。 在本文中&#xff0c;将介绍如何使用名为…

最新win11配置cuda以及cudnn补丁教程

1、首先使用指令 nvidia-smi 查看电脑支持的**最高cuda**版本&#xff0c;例如&#xff1a;本机 12.2 2、进入CUDA下载cuda安装包 https://developer.nvidia.com/cuda-toolkit-archive 2、点击上方绿色的链接&#xff0c;按照图中序号选择的即可&#xff0c;最后点击下载。 …