首页 > 其他分享 >迭代器模式揭秘:如何优雅应对数据遍历

迭代器模式揭秘:如何优雅应对数据遍历

时间:2023-12-25 21:01:39浏览次数:26  
标签:遍历 迭代 名册 接口 public 集合 揭秘


迭代器模式揭秘:如何优雅应对数据遍历_java

推荐语

在这篇文章中,深入探讨了迭代器模式的核心原理和实战应用。通过清晰而有条理的解释,读者小伙伴可以领悟到迭代器模式在数据遍历和管理方面的强大能力。无论是初学者还是有经验的开发者,都能从这篇文章中获得实用的知识和技巧,进一步提升代码的可读性和可维护性。

什么是迭代器模式

迭代器模式(Iterator pattern)是一种对象行为型设计模式,它提供了一种方法来顺序访问聚合对象中的元素,而又不暴露该对象的内部表示,同时也可以将迭代逻辑与聚合对象的实现分离,增强了代码的可维护性和可扩展性。

迭代器模式的特征如下:

  • 迭代器模式允许一个聚合对象公开它的每一个元素,而又不暴露其内部表示,即它提供了一种方法来遍历聚合对象中的每一个元素,而不需要了解该对象的内部结构或实现。
  • 迭代器模式的主要目的是将迭代逻辑封装在迭代器对象中,而不是将该逻辑嵌入到聚合对象中。这样可以将迭代逻辑与聚合对象的实现分离,使得它们可以独立地改变和扩展。
  • 迭代器模式是通过定义一个迭代器接口来实现的。该接口提供了访问和遍历聚合对象的元素的方法。聚合对象则需要实现一个能够创建相应迭代器的接口。

迭代器模式的核心原理

迭代器模式的核心原理是将数据遍历的过程封装成一个迭代器对象,以便对该数据结构进行统一的管理和操作。通过迭代器,可以实现对集合内部数据的访问,而不暴露其内部实现细节。

迭代器模式的核心角色:

  1. 迭代器接口(Iterator):定义了访问和遍历集合元素的方法,为使用者提供了一种方便的方式来遍历集合中的元素,而不必关心集合的实现细节,如:是否能够继续遍历下一个元素、取出下一个元素等。
  2. 具体迭代器(ConcreteIterator):持有一个具体的集合,并且实现了迭代器接口,实际负责按照特定的顺序遍历集合中的元素,并将遍历结果反馈给使用者。
  3. 集合接口(Aggregate):定义了创建迭代器的抽象方法,隐藏了具体集合的表示和实现,具体的表示和实现由具体集合类来负责实现;
  4. 具体集合(ConcreteAggregate):该类实现了集合接口,创建出具体的迭代器对象,供使用者通过迭代器来访问和遍历集合元素。当然,作为具体集合,需要提供具体的添加、移除、查询集合长度的具体方法;

迭代器模式揭秘:如何优雅应对数据遍历_java_02

迭代器模式如何实现

需求描述

回想在上大学的时候,老师在教学活动中都会拥有一个学生的花名册,上面有学生的姓名、学号等信息,每次上课前都要先按照名册上记录的顺序逐一点名,如果帮老师写一个自动点名的程序,那么这个时候使用迭代器模式绝对是非常不错的一个选择。那么具体怎么实现呢?

实现方法

迭代器模式揭秘:如何优雅应对数据遍历_设计模式_03

1、定义一个迭代器接口,即名册迭代器接口,主要有两个抽象方法:判断是否下一个元素可以遍历、遍历取出下一个元素;

/** 
* 名删除迭代器抽象接口 
* @param <T> 
*/
public interface RosterInterator<T> {
    /**
     * 是否有下一个元素
     * @return
     */
    boolean hasNext();

    /**
     * 取出下一个元素
     * @return
     */
    T next();
}

2、定义一个集合接口:抽象名册接口,定义一个抽象方法:获取名册迭代器;

/** 
* 名册 
*/
public interface Roster<T> {
    /**
     * 获取迭代器
     * @return
     */
    RosterInterator<T> getInterator();
}

3、定义具体的集合,即具体的学生名册类,具体的学生名册类除了正常可以往学生名册上添加、移除学生,查询学生名册上人员数量方法外,还要实现抽象名册接口的获取名册迭代器的方法,在方法中创建具体的迭代器对象;

/** 
* 学生类 
*/
@Data
public class Student {
    private String stuNo;
    private String name;

    public Student(String stuNo, String name) {
        this.stuNo = stuNo;
        this.name = name;
    }
}
/**
* 学生名册 
*/
@Data
public class StudentRoster implements Roster<Student>{
    /**
     * 学生对象集合
     */
    private List<Student> list=new ArrayList<>();

    /**
     * 添加学生
     * @param student
     */
    public void add(Student student){
        this.list.add(student);
    }

    /**
     * 移除学生
     * @param student
     */
    public void remove(Student student){
        this.list.remove(student);
    }

    /**
     * 学生名册学生对象数量
     * @return
     */
    public Integer size(){
        return this.list.size();
    }
    @Override
    public RosterInterator<Student> getInterator() {
        return new StudentRosterInterator(this);
    }
}

4、定义集合迭代器实现,即具体的学生名册迭代器,具体的学生迭代器会持有具体的学生名册,并实现名册迭代器定义的两个抽象方法,即具体的判断是否有下一个元素可以遍历、遍历取出下一个元素;

/** 
* 学生名册迭代器
*/
@Data
public class StudentRosterInterator implements RosterInterator<Student>{
    /**
     * 学生名册
     */
    private StudentRoster roster;
    /**
     * 索引位置
     */
    private Integer index=0;

    public StudentRosterInterator(StudentRoster roster) {
        this.roster = roster;
    }

    @Override
    public boolean hasNext() {
        return this.roster.size() > index;
    }

    @Override
    public Student next() {
        Student student = this.roster.getList().get(index);
        index++;
        return student;
    }
}

5、编写客户端类

public class StudentClient {  
    public static void main(String[] args) {     
        StudentRoster studentRoster=new StudentRoster();      
        Student stu1 = new Student("s001", "小明");
        Student stu2 = new Student("s002", "小红");
        Student stu3 = new Student("s003", "小刚");
        studentRoster.add(stu1);
        studentRoster.add(stu2);
        studentRoster.add(stu3);
        RosterInterator<Student> rosterInterator=new StudentRosterInterator(studentRoster);
        while (rosterInterator.hasNext()) {
            Student student = rosterInterator.next();
            System.out.println(student.getStuNo()+":"+student.getName());
        }
    }
}

如何扩展

老师上课的时候会根据学生画名册点名,那么学校领导在给老师们开会的时候,有没有可能也会点一个名,看年哪位老师没有到?当然会。假如也要帮校长实现一个对老师们的点名程序,应该怎么在原先的基础上扩展呢?其实很简单,保持原有的抽象名册迭代器接口、抽象名册接口不变,再分别实现老师画名册、老师画名册迭代器就可。而且这一过程完全符合开闭原则,不会对原来的程序造成任何影响,这就是设计模式的魅力。

迭代器模式揭秘:如何优雅应对数据遍历_迭代器模式_04

/** 
* 老师类
*/
@Data
public class Teacher {
    private String teacNo;
    private String name;

    public Teacher(String teacNo, String name) {
        this.teacNo = teacNo;
        this.name = name;
    }
}
/** 
* 老师名册
*/
@Data
public class TeacherRoster implements Roster<Teacher>{
    /**
     * 老师对象集合
     */
    private List<Teacher> list=new ArrayList<>();

    /**
     * 添加老师
     * @param teacher
     */
    public void add(Teacher teacher){
        this.list.add(teacher);
    }

    /**
     * 移除老师
     * @param teacher
     */
    public void remove(Teacher teacher){
        this.list.remove(teacher);
    }

    /**
     * 老师名册中对象数量
     * @return
     */
    public Integer size(){
        return this.list.size();
    }

    @Override
    public RosterInterator<Teacher> getInterator() {
        return new TeacherRosterInterator(this) ;
    }
}
/**
* 老师名册迭代器 
*/
@Data
public class TeacherRosterInterator implements RosterInterator<Teacher> {

    private TeacherRoster teacherRoster;
    private Integer index = 0;


    public TeacherRosterInterator(TeacherRoster teacherRoster) {
        this.teacherRoster = teacherRoster;
    }

    @Override
    public boolean hasNext() {
        return this.teacherRoster.size() > index;
    }

    @Override
    public Teacher next() {
        Teacher teacher = this.teacherRoster.getList().get(index);
        index++;
        return teacher;
    }
}
public class TeacherClent {   
    public static void main(String[] args) {    
        TeacherRoster teacherRoster=new TeacherRoster();   
        Teacher t1 = new Teacher("t001", "王老师");       
        Teacher t2 = new Teacher("t002", "李老师");
        Teacher t3 = new Teacher("t003", "张老师");
        teacherRoster.add(t1);
        teacherRoster.add(t2);
        teacherRoster.add(t3);
        RosterInterator<Teacher> rosterInterator=new TeacherRosterInterator(teacherRoster);
        while (rosterInterator.hasNext()) {
            Teacher teacher = rosterInterator.next();
            System.out.println(teacher.getTeacNo()+":"+teacher.getName());
        }
    }
}

迭代器模式的适用场景

业务场景具有下面的特征就可以使用迭代器模式:

  • 需要遍历集合对象中的元素,而不暴露该对象的内部表示的场景。通过使用迭代器模式,可以在不暴露集合对象内部结构的情况下,顺序访问集合对象中的各个元素。
  • 需要为遍历不同的集合结构提供统一接口的场景。迭代器模式可以提供一种统一的接口,用于遍历不同的集合结构,从而使得代码更加灵活和可扩展。

有没有比较具体的业务场景示例呢?当然有,比如:

  1. 在物流系统中,可以使用迭代器模式来遍历物品。例如,在传送带上,不管传送的是什么物品,都被打包成一个一个的箱子并且有一个统一的二维码。这样我们不需要关心箱子里是什么物品,只需要一个一个检查发送的目的地即可。
  2. 在图片播放器中,可以使用迭代器模式来遍历图片。这样可以在不暴露图片内部结构的情况下,顺序访问图片中的各个元素。
  3. 图书馆管理系统:在图书馆中,需要对书籍进行遍历,可以使用迭代器模式来处理。通过定义一个统一的迭代器接口,不同的数据结构(如数组、链表等)都可以使用相同的迭代器接口来遍历书籍,同时保持内部实现的封装。
  4. 电商网站:在电商网站中,通常需要对商品进行展示和遍历。使用迭代器模式可以实现对商品的顺序访问,而不暴露商品内部表示。
  5. 金融系统:在金融系统中,需要对账户、交易等进行处理。使用迭代器模式可以实现对账户或交易的顺序访问,而不暴露其内部表示。
  6. 音乐播放器:在音乐播放器中,需要遍历音乐库中的歌曲。使用迭代器模式可以实现对歌曲的顺序访问,而不暴露歌曲内部表示。

总结

优点

  1. 分离集合与迭代逻辑:迭代器模式将集合对象与遍历逻辑分离,使得它们可以独立变化。集合对象只需要实现迭代器接口,而客户端只需要通过迭代器进行遍历操作,从而实现了解耦和模块化。
  2. 统一遍历接口:迭代器模式定义了一组统一的遍历接口,使得客户端可以以相同的方式对待不同类型的集合对象。无论是数组、链表、树状结构还是其他自定义集合,只要它们提供了符合迭代器接口的迭代器对象,就可以使用迭代器模式进行遍历,提高了代码的灵活性和可复用性。
  3. 简化客户端代码:使用迭代器模式可以简化客户端代码,减少了对集合内部结构的直接操作,只需要通过迭代器对象进行遍历操作。

缺点

  1. 可能增加代码复杂度:使用迭代器模式可能会增加一些额外的代码复杂度,例如需要定义迭代器接口、具体迭代器实现类等。
  2. 限制集合对象的类型:迭代器模式通常只适用于集合类型的聚合对象,不能很好地处理其他类型的聚合对象,例如树形结构、图形结构等。
  3. 可能增加内存开销:使用迭代器模式可能会增加一些额外的内存开销,例如需要创建迭代器对象等。

总之,迭代器模式在许多业务场景中都有应用,可以实现对集合对象的顺序访问,而不暴露其内部表示,可以使得代码更加清晰、简洁、易于维护。

标签:遍历,迭代,名册,接口,public,集合,揭秘
From: https://blog.51cto.com/fanfu1024/8972167

相关文章

  • 函数的递归和迭代
    我们学习函数递归和迭代往往需要掌握许多的数学规律,公式定律,下面我们通过两个递归,迭代相关的经典例题来了解递归和迭代的思想。今天的主角是我们的汉诺塔和青蛙跳台阶。(1)汉诺塔:相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),......
  • 揭秘抖音视频详情API:电商行业的制胜法宝与实时数据获取的奥秘
    一、引言随着社交媒体的普及和电商行业的快速发展,抖音等短视频平台已经成为电商企业获取用户、推广产品和了解市场趋势的重要渠道。获取抖音视频详情API对于电商行业来说具有重要意义,它可以帮助企业实时获取抖音平台上的视频数据,洞察用户需求,优化产品策略,制定精准营销计划,从而提升......
  • 揭秘JVS低代码平台:如何通过行内按钮逻辑引擎配置,实现高效文件模板替换下载
    在当今数字化的时代,各行各业都在寻求更高效、更便捷的工作方式。对于业务应用来说,将线下操作转化为线上流程是提升效率的关键。在业务应用中通常需要把行数据某字段赋值到一个文件模板上,用户下载该文件模板用于盖章或签字等线下操作。这样的场景在JVS低代码平台上可以通过行内按钮......
  • 给出中序和按层遍历,求该树的先序遍历,后序遍历,叶子结点。
    一切的核心是怎么利用中序和按层遍历构建二叉树?1.优化空间很大,可以提前预处理记录每个数对应的位置,还可以vis数组记录这个点是不是已经作为根了。2.我们考虑到每次找到当前中序要处理区间,里面的数记为集合mid,我们从前到后看层序遍历中的哪个数最先出现在mid中。那么这个数就是当......
  • 二叉树给出先序和中序遍历序列,求和树 要求输出中序遍历序列;
    1.就算不知道用vector的初始化,也可以手动赋值创建子数组。2.不断找到当前序列对应的根节点,计算他的子节点的总和,在这样递归处理过程中,注意要中序输出,所以对于是先遍历完左子树,然后输出答案,然后遍历右子树#include<bits/stdc++.h>usingnamespacestd;#definelllonglong//......
  • 观察者模式揭秘:实现松耦合的事件通知机制
    推荐语本篇文章深度剖析了观察者模式的核心原理及其在软件开发中的重要应用,通过清晰而深入的讲解,读者小伙伴可以深入理解观察者模式如何实现松耦合的事件通知机制,从而构建更灵活、可扩展的软件系统。本文既适合希望深入了解设计模式的专业人士,也适合希望提升代码质量和可维护性的开......
  • 深入了解 Linux 网卡和网口:揭秘网络接口的奥秘
    实际工作中,把服务器关机了,网线从一个网口移动到了其他网口,导致再开机后,服务器无法联网了。由于缺少王工的支持,这使我开始关注网络技术。先总结下常用的概念和操作吧。1.网卡和网口的对应关系在Linux系统中,网卡(NetworkInterfaceCard,NIC)与网络接口(网口)之间存在紧密的对应关系。......
  • re | 通过PEB遍历进程模块
    re|通过PEB遍历进程模块最近在设计实验,重新写一些代码存一下:使用vc6编译通过。比较好的参考文章:https://www.cnblogs.com/bokernb/p/6404795.html#include<stdio.h>#include<windows.h>/*typedefstruct_LIST_ENTRY{struct_LIST_ENTRY*Flink;struct_LIST_......
  • 320二叉树的不同形态(已知层次遍历和中序遍历创建二叉树;输出叶子结点(从左到右);后序遍历)
    题目:二叉树的不同形态问题描述给定二叉树T(树深度H<=10,深度从1开始,结点个数N<1024,结点编号1~N)的层次遍历序列和中序遍历序列,输出T从左向右叶子结点以及二叉树先序和后序遍历序列。输入格式输入共三行:第一行是整数n,表示二叉树中的结点数目;第二行有n个整数,表示该二叉树的层次遍......
  • 318二叉树遍历
    1#include<stdio.h>2#include<string.h>3#include<stdlib.h>4typedefstructtreenode{5chardata;6structtreenode*lchild;7structtreenode*rchild;8}treenode,*tree;910treecreateTree(char*preorder,char......