设计模式之【迭代器模式】,对集合访问的统一

news2024/12/27 13:18:12

文章目录

  • 一、什么是迭代器模式
    • 1、迭代器模式使用场景
    • 2、迭代器模式的优势
    • 3、迭代器模式的四种角色
    • 4、迭代器模式的优缺点
  • 二、手写一个迭代器
    • 1、迭代器模式的一般写法
    • 2、课程迭代器
  • 三、源码中的迭代器
    • 1、ArrayList

一、什么是迭代器模式

迭代器模式(Iterator Pattern)又称为游标模式(Cursor Pattern),它提供一种顺序访问集合/容器对象元素的方法,而又无需暴露集合内部表示。迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构,属于行为型模式。

迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。

1、迭代器模式使用场景

我们把多个对象聚在一起形成的总体称之为集合(Aggregate),集合对象是能够包容一组对象的容器对象。不同的集合其内部元素的聚合结构可能不同,而迭代器模式屏蔽了内部元素获取细节,为外部提供一致的元素访问行为,解耦了元素迭代与集合对象间的耦合,并且通过提供不同的迭代器,可以为同个集合对象提供不同顺序的元素访问行为(比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等),扩展了集合对象元素迭代功能,符合开闭原则。

迭代器模式适用于以下场景:

  • 访问一个集合对象的内容而无需暴露它的内部表示;
  • 为遍历不同的集合结构提供一个统一的访问接口;
  • 需要为聚合对象提供多种遍历方式。

2、迭代器模式的优势

一般来讲,遍历集合数据有三种方法:for 循环、foreach 循环、iterator 迭代器。对于这三种方式,具体的代码如下所示:

List<String> names = new ArrayList<>();
names.add("aaa");
names.add("bbb");
names.add("ccc");
// 第一种遍历方式:for循环
for (int i = 0; i < names.size(); i++) {
  System.out.print(names.get(i) + ",");
}
// 第二种遍历方式:foreach循环
for (String name : names) {
  System.out.print(name + ",")
}
// 第三种遍历方式:迭代器遍历
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
  System.out.print(iterator.next() + ",");//Java中的迭代器接口是第二种定义方式,next()既移动游标又返回数据
}

实际上,foreach 循环只是一个语法糖而已,底层是基于迭代器来实现的。也就是说,上面代码中的第二种遍历方式(foreach 循环代码)的底层实现,就是第三种遍历方式(迭代器遍历代码)。这两种遍历方式可以看作同一种遍历方式,也就是迭代器遍历方式。

从上面的代码来看,for 循环遍历方式比起迭代器遍历方式,代码看起来更加简洁。那我们为什么还要用迭代器来遍历容器呢?为什么还要给容器设计对应的迭代器呢?原因有以下三个。

首先,对于类似数组和链表这样的数据结构,遍历方式比较简单,直接使用 for 循环来遍历就足够了。但是,对于复杂的数据结构(比如树、图)来说,有各种复杂的遍历方式。比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等。如果由客户端代码来实现这些遍历算法,势必增加开发成本,而且容易写错。如果将这部分遍历的逻辑写到容器类中,也会导致容器类代码的复杂性。

我们可以将遍历操作拆分到迭代器类中。比如,针对图的遍历,我们就可以定义 DFSIterator、BFSIterator 两个迭代器类,让它们分别来实现深度优先遍历和广度优先遍历。

其次,将游标指向的当前位置等信息,存储在迭代器类中,每个迭代器独享游标信息。这样,我们就可以创建多个不同的迭代器,同时对同一个容器进行遍历而互不影响。

最后,容器和迭代器都提供了抽象的接口,方便我们在开发的时候,基于接口而非具体的实现编程。当需要切换新的遍历算法的时候,比如,从前往后遍历链表切换成从后往前遍历链表,客户端代码只需要将迭代器类从 LinkedIterator 切换为 ReversedLinkedIterator 即可,其他代码都不需要修改。除此之外,添加新的遍历算法,我们只需要扩展新的迭代器类,也更符合开闭原则。

3、迭代器模式的四种角色

在这里插入图片描述

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

4、迭代器模式的优缺点

优点:

  • 多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的集合对象;
  • 简化集合对象接口:迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无须关心具体迭代行为;
  • 元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使得同种元素聚合结构可以有不同的迭代行为(比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等);
  • 解耦迭代与集合:迭代器模式封装了具体的迭代算法,迭代算法的变化,不会影响到集合对象的架构。

缺点:

  • 对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐。

在日常开发当中,我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供给我们的API完全够用。

二、手写一个迭代器

1、迭代器模式的一般写法

//抽象迭代器
public interface Iterator<E> {
    E next();
    boolean hasNext();
}

//具体迭代器
public class ConcreteIterator<E> implements Iterator<E> {
    private List<E> list;
    private int cursor = 0;

    public ConcreteIterator(List<E> list) {
        this.list = list;
    }

    public E next() {
        return this.list.get(this.cursor ++);
    }

    public boolean hasNext() {
        return this.cursor < this.list.size();
    }
}
//抽象容器
public interface IAggregate<E> {
    boolean add(E element);

    boolean remove(E element);

    Iterator<E> iterator();
}

//具体容器
public class ConcreteAggregate<E> implements IAggregate<E> {
    private List<E> list = new ArrayList<E>();

    public boolean add(E element) {
        return this.list.add(element);
    }

    public boolean remove(E element) {
        return this.list.remove(element);
    }

    public Iterator<E> iterator() {
        return new ConcreteIterator<E>(this.list);
    }
}
public class Test {
    public static void main(String[] args) {
        //来一个容器对象
        IAggregate<String> aggregate = new ConcreteAggregate<String>();
        //添加元素
        aggregate.add("one");
        aggregate.add("two");
        aggregate.add("three");
        //获取容器对象迭代器
        Iterator<String> iterator = aggregate.iterator();
        //遍历
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }

    }
}

2、课程迭代器

// 迭代器接口
public interface Iterator<E> {
    E next();
    boolean hasNext();
}
public class IteratorImpl<E> implements Iterator<E> {
    private List<E> list;
    private int cursor;
    private E element;

    public IteratorImpl(List<E> list) {
        this.list = list;
    }

    public E next() {
        System.out.print("当前位置 " + cursor + " : ");
        element = list.get(cursor);
        cursor ++;
        return element;
    }

    public boolean hasNext() {
        if(cursor > list.size() - 1){
            return false;
        }
        return true;
    }
}

// 课程抽象容器
public interface ICourseAggregate {
    void add(Course course);
    void remove(Course course);
    Iterator<Course> iterator();
}
// 具体容器
public class CourseAggregateImpl implements ICourseAggregate {
    private List courseList;

    public CourseAggregateImpl() {
        this.courseList = new ArrayList();
    }

    public void add(Course course) {
        courseList.add(course);
    }

    public void remove(Course course) {
        courseList.remove(course);
    }

    public Iterator<Course> iterator() {
        return new IteratorImpl<Course>(courseList);
    }
}
public class Test {
    public static void main(String[] args) {
        Course java = new Course("Java架构");
        Course javaBase = new Course("Java基础");
        Course design = new Course("设计模式");
        Course ai = new Course("人工智能");

        ICourseAggregate aggregate = new CourseAggregateImpl();
        aggregate.add(java);
        aggregate.add(javaBase);
        aggregate.add(design);
        aggregate.add(ai);

        System.out.println("===========课程列表==========");
        printCourse(aggregate);

        aggregate.remove(ai);

        System.out.println("===========删除操作之后的课程列表==========");
        printCourse(aggregate);
    }

    private static void printCourse(ICourseAggregate aggregate) {
        Iterator<Course> i = aggregate.iterator();
        while (i.hasNext()){
            Course course = i.next();
            System.out.println("《" + course.getName()  + "》");
        }
    }
}

三、源码中的迭代器

在日常开发当中,我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供给我们的API完全够用。

1、ArrayList

以下代码就是我们使用ArrayList的迭代器进行迭代的案例:

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {
	System.out.println(iterator.next());
}

ArrayList的结构和我们上面的实例结构是一样的,只不过将其迭代器放到了内部类中了:

  • List:抽象聚合类
  • ArrayList:具体的聚合类
  • Iterator:抽象迭代器
  • list.iterator():返回的是实现了 Iterator 接口的具体迭代器对象

我们看一下Iterator的源码:

public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

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

从Iterator代码中,主要定义了hasNext()和next()、remove()方法,和我们写的完全一致。

我们发现,迭代器模式和组合模式,两者似乎存在一定的相似性。组合模式解决的是统一树形结构各层次访问接口,迭代器模式解决的是统一各集合对象元素遍历接口。虽然他们的适配场景不同,但是核心理念是相通的。

Iterator的实现类,在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;

    // prevent creating a synthetic constructor
    Itr() {}

    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];
    }

    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();
        }
    }

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i < size) {
            final Object[] es = elementData;
            if (i >= es.length)
                throw new ConcurrentModificationException();
            for (; i < size && modCount == expectedModCount; i++)
                action.accept(elementAt(es, i));
            // update once at end to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

当我们在使用JAVA开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现java.util.Iterable 并实现其中的iterator()方法使其返回一个 java.util.Iterator 的实现类就可以了。

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

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

相关文章

(5.12-5.18)【大数据新闻速递】

关 注gzh“大数据食铁兽”&#xff0c;了解更多大数据快讯 【打造全国首个数据要素产业集聚区&#xff01;浦东数据要素产业规模2025年将达1000亿元】 5月16日&#xff0c;“数启浦东”2023浦东新区数据要素产业主题系列活动启动。记者获悉&#xff0c;《张江数据要素产业集聚…

【实用工具】Guava EventBus(事件总线)快速入门

介绍 EventBus是Guava的事件处理机制&#xff0c;是设计模式中的观察者模式&#xff08;生产/消费者编程模型&#xff09;的优雅实现。对于事件监听和发布订阅模式&#xff0c;EventBus是一个非常优雅和简单解决方案&#xff0c;我们不用创建复杂的类和接口层次结构。 Java案…

树莓派报错Oops - unable to determine board type . . .model:17

报错原因 改变方法 cd /tmp wget https://project-downloads.drogon.net/wiringpi-latest.deb sudo dpkg -i wiringpi-latest.deb成功

技术的力量:如何用数据驱动实现设备健康管理

在当今数字化时代&#xff0c;设备管理部门面临着日益复杂的挑战。传统的设备管理方法已经无法满足快速发展的需求&#xff0c;而数字化转型成为了提升效率、降低成本、增强竞争力的关键路径。 本文将介绍设备管理部门数字化转型的痛点、可参考的蓝图规划以及PreMaint平台在实现…

浅谈面向无线通信的微波毫米波无源天线及器件

一、背景 本文谈及的微波毫米波&#xff08;下简称微波&#xff09;产品主要是指工作在4&#xff5e;86GHz频段的无源天线和器件。它们使通信系统在不需要电源模块的情况下具备较高的动态范围和实现宽带模拟信道传输&#xff0c;属于现代点对点无线通信系统中核心天馈部件。文…

5年测试开发,跳槽薪资25k变成30k,总结的这些面试题,你会哪些?

每年的金三银四都是各大公司招聘程序员的最佳时期&#xff0c;在这段时间内有好多程序员会为了面试而发愁&#xff0c;不知道如何才能收到好的offer&#xff0c;拿到理想的薪资&#xff0c;实现自我的人生价值&#xff01; 我想告诉大家的是&#xff0c;其实都不用愁的&#xf…

AOSP构建、编译基础理解

AOSP构建、编译基础理解 构建系统 参考这篇文章&#xff0c;写的比较好&#xff0c;我就不狗尾续貂了&#xff01; android build system编译系统概述 source build/envsetup.sh之后的事情 source也就是执行build/envsetup.sh里面的脚本&#xff0c;改脚本定义许多命令&…

​GPT充当大脑,指挥多个模型协作完成各类任务,通用系统AutoML-GPT来了

使用 ChatGPT 实现通用人工智能&#xff0c;思路打开了。 当前&#xff0c;AI 模型虽然已经涉及非常广泛的应用领域&#xff0c;但大部分 AI 模型是为特定任务而设计的&#xff0c;它们往往需要大量的人力来完成正确的模型架构、优化算法和超参数。ChatGPT、GPT-4 爆火之后&…

ST典型碳化硅MOSFET驱动应用方案

ST典型碳化硅MOSFET驱动应用方案 1.栅极驱动器规格和功能实现 参考资料&#xff1a;ST官网应用手册《AN4671》 作者&#xff1a;Xiou 1.栅极驱动器规格和功能实现 以下是对栅极驱动要求的简短列表&#xff1a; dv / dt 的瞬变抗扰度&#xff1a;在整个温度范围内 50 V/ns。 …

超级简单的开源saas后台系统管理框架Vite+Vue3

大家好&#xff0c;今天我给大家带来一款超简saas后台管理系统框架&#xff0c;他是一款快速开发SAAS通用管理系统后台框架&#xff0c;前端采用最新的技术栈ViteTypeScriptVue3ElementPlus最流行技术架构&#xff0c;后台结合PHP8、Java SDK、Python等主流后端语言搭建&#x…

黑盒测试方法: 从原理到实战

文章目录 一. 如何设计测试用例二. 常用黑盒测试方法1. 基于需求设计的测试用例2. 等价类划分法3. 边界值4. 判定表分析法 (因果分析法)5. 正交排列6. 场景设计法7. 用例场景示例8. 错误猜测法 三. 补充案例Fiddler实现弱网测试水杯测试用例微信朋友圈测试用例淘宝购物车测试用…

ESP8266连接 TLink 云平台

1.硬件准备 &#xff08;1&#xff09;正点原子 ATK-ESP-01 WIFI 模块 &#xff08;2&#xff09;正点原子 STM32F103ZET6精英板子 &#xff08;3&#xff09;USB转TTL模块 2.烧录固件 &#xff08;1&#xff09;烧录软件和固件都可以在正点原子增值资料包找到。 &#xff08;2…

网络安全基础--dns劫持及IP信息收集

0x01 验证是否存在CDN 方法1&#xff1a; 很简单&#xff0c;使用各种多地 ping 的服务&#xff0c;查看对应 IP 地址是否唯一&#xff0c;如果不唯一多半是使用了CDN&#xff0c; 多地 Ping 网站有&#xff1a;多个地点Ping服务器,网站测速 - 站长工具网站测速工具_超级ping…

创建python虚拟环境的两种方法

创建python虚拟环境的两种方法 一、anaconda环境下1、检查是否安装了anaconda2、创建虚拟环境3、激活虚拟环境4、其他命令 二、python纯净环境下1. 安装virtualenv2. 创建虚拟环境3. 激活虚拟环境 一、anaconda环境下 1、检查是否安装了anaconda 只有在anaconda环境下才能创建…

近百个最新免费chatgpt访问集合,包含国内直接访问和国外升级版本

近百个最新免费chatgpt访问集合&#xff0c;包含国内直接访问和国外升级版本。 ChatGPT是一个基于人工智能的聊天机器人&#xff0c;它可以与用户进行自然语言交互。ChatGPT使用了最新的自然语言处理技术&#xff0c;包括深度学习和神经网络&#xff0c;以便更好地理解用户的…

慎投,5月有4本SCIE期刊被剔除(附SCI/SSCI目录下载)

2023年5月SCI、SSCI期刊目录更新 2023年5月18日&#xff0c;科睿唯安更新了WOS期刊目录&#xff0c;继上次4月WOS期刊目录剔除8本SCIE&SSCI期刊之后&#xff0c;此次5月更新又有4本SCIE期刊发生变动&#xff0c;其中有1本期刊被踢出SCIE数据库&#xff0c;3本期刊更改了名…

pdf怎么转换成ppt文件,5种方法任你选

pdf怎么转换成ppt文件&#xff1f;想必这是我们办公过程中非常常见的问题吧。众所周知&#xff0c;PDF文件格式通常用于存储文档&#xff0c;其内容可能是图像、文字或表格&#xff0c;展示在一个页面上。PPT文件格式通常用于创建演示文稿&#xff0c;其中每个页面都是幻灯片&a…

探秘音乐疗法——基于音乐的喂养环境对小鼠肠道菌群影响的研究

音乐对身心的影响 近年来&#xff0c;环境和动物生理及心理的相关研究越来越多。环境因素的丰富性和多样性是改善动物生理和心理状态的重要研究参数。 环境因素指的是正常环境&#xff0c;在这种环境中&#xff0c;动物通过获得环境激励以做出有益的增强&#xff0c;使它们能够…

怎么把两个pdf合并成一个?三种合并方法任你选择

PDF 格式是一种常见的跨平台文件格式&#xff0c;因此在日常生活和工作中&#xff0c;我们可能需要处理或编辑多个 PDF 文件&#xff0c;并将它们合并为一个文件&#xff0c;以方便查阅和共享。因此&#xff0c;将两个PDF文件合并是非常重要的。首先&#xff0c;两个PDF合并成一…

2023年最新整理渗透测试面试题

1、include、include_once、require、 require_once区别 参考答案&#xff1a; 1、require()和require_once()函数&#xff1a; &#xff08;1&#xff09;require()函数引入文件不存在时&#xff0c;将立即退出程序&#xff0c;不再向下执行。 &#xff08;2&#xff09;…