当前位置: 首页 > news >正文

迭代器模式:理解与实践

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看

(一)什么是迭代器模式

迭代器模式应该是23种设计模式中,程序员最容易理解的设计模式了,因为迭代器模式在日常的开发过程中经常使用到。以最常见的循环为例:

for(int i=0;i<arr.length;i++){
    System.out.print(arr[i]);
}

上面的这段代码通过循环逐一将arr的下标加1,从而实现了遍历数组的功能。

但是通过循环实现遍历的话依赖于集合对象内部,因此就有了一种能顺序访问集合对象中各个元素,又无需依赖对象内部表示的设计模式–迭代器模式。

(二)迭代器模式中的角色

迭代器模式中的角色主要有4种:

1、迭代器接口(Iterator):定义访问和遍历集合元素的接口,一般包含next()和hasNext()方法。

2、具体迭代器(ConcreteIterator):该角色用于实现迭代器接口,迭代器的核心遍历逻辑在这里实现。

3、集合接口(Aggregate):集合接口定义了创建迭代器的接口方法,内部定义了iterator方法。

4、具体集合(ConcreteAggregate):该角色用于实现集合接口,他会创建出具体的Iterator角色。

看到这里如果你觉得比较疑惑不要紧,下面会通过代码的方式来加深理解。

(三)迭代器模式的代码实现

首先说一下这段代码的场景,定义了一个教室的类,又定义了学生的类,实现遍历教室中学生的功能。

代码列表如下:

interface Aggregate:集合接口
interface Iterator:迭代器接口
class Classroom:教室类,实现集合接口,属于具体的集合
class ClassroomIterator:教室迭代器,实现迭代器接口,属于具体的迭代器
class Student:学生类

首先把迭代器模式中的两个接口角色定义出来:

public interface Aggregate {
    Iterator iterator();
}

public interface Iterator {
    boolean hasNext();
    Object next();
}

接着定义学生类:

@Data
@AllArgsConstructor
public class Student {
    private String name;
}

接着定义教室类,在教室类中我们定义了Student集合,以及当前的集合长度和最大长度。同时实现Aggregate接口的iterator方法,这个方法将会返回一个迭代器对象。这个迭代器对象由ClassroomIterator提供

public class Classroom implements Aggregate{
    private Student[] students;
    private int length=0;
    private int maxSize;

    public Classroom(int maxSize){
        this.maxSize=maxSize;
        students=new Student[maxSize];
    }

    public Student getStudent(int index){
        return students[index];
    }

    public boolean addStudent(Student student){
        if (length>=maxSize){
            return false;
        }
        this.students[length]=student;
        length++;
        return true;
    }

    public int getLength(){
        return this.length;
    }

    @Override
    public Iterator iterator() {
        return new ClassroomIterator(this);
    }
}

最后就是ClassroomIterator对象了,ClassroomIterator属于迭代器的具体实现,这里需要实现hasNext方法和next方法

public class ClassroomIterator implements Iterator{
    private Classroom classroom;
    private int index;

    public ClassroomIterator(Classroom classroom){
        this.classroom=classroom;
        this.index=0;
    }
    @Override
    public boolean hasNext() {
        if (this.index<classroom.getLength()){
            return true;
        }
        return false;
    }

    @Override
    public Object next() {
        Student student = classroom.getStudent(index);
        index++;
        return student;
    }
}

最后就是使用了,通过迭代器对象,我们可以直接遍历classroom对象:

public static void main(String[] args) {
    Classroom classroom=new Classroom(3);
    classroom.addStudent(new Student("张三"));
    classroom.addStudent(new Student("李四"));
    classroom.addStudent(new Student("王五"));
    Iterator iterator = classroom.iterator();
    while (iterator.hasNext()){
        Student next = (Student) iterator.next();
        System.out.println(next.getName());
    }
}

(四)迭代器模式的作用

看到这里很多人可能会有疑问,写了一堆,用循环不是更方便吗?迭代器模式最大的作用是将遍历和具体的实现分开,以上面的测试方法为例,遍历时我们始终只用到了iterator对象,而没有用到classroom,这就意味着我们之后可以完全复用这段代码实现遍历。

另一方面,如果我们发现在classroom里使用数组存储student,后续无法扩容,想改为List集合,这个时候我们只需要修改ClassroomIterator和Classroom这两个具体实现角色即可。而不用对使用中的代码做任何修改,就比如上面这段测试遍历代码不需要任何变动。如果用的是for循环或者while循环,就意味着所有用到循环的地方都需要修改代码。

(五)迭代器模式在源码中的应用

迭代器模式的应用我们在敲代码时肯定都用过,迭代器模式最佳实践就是JDK中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());
    }
}

具体的迭代器实现最常用的就是集合,随便找个ArrayList看一下源码:

整体的实现逻辑和我们上面实现的基本上十分相似。

(五)总结

在学习设计模式的时候,会慢慢开始理解为什么要设计接口,而不是直接写各种类。如果用具体的类去解决一个个需求,就会导致类之间的强依赖,这些类也难以被拆分出来作为组件复用。我是鱼仔,我们下期再见!

相关文章:

  • [转]MySQL排序原理与案例分析
  • 深入浅出CMS垃圾收集器
  • php.ini中的session配置说明
  • 如何上传自己的jar包到maven中央仓库(2021最新版)
  • ElasticSearch聚合查询Restful语法和JavaApi详解(基于ES7.6)
  • 《偷影子的人》
  • 模板方法设计模式理论与应用
  • 跟我一起数据挖掘(13)——矩阵分解
  • 用了MybatisPlus后,我很久没有手写sql了
  • 两张图让你快速读懂JVM字节码指令
  • Android Bitmap面面观
  • 用几张图深度剖析Java运行时数据区
  • HDOJ(HDU) 2519 新生晚会(组合公式)
  • 从JVM角度思考--如何预估线上环境机器资源大小
  • 1到3年的Java开发工程师应该如何准备面试
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 30天自制操作系统-2
  • Android 架构优化~MVP 架构改造
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • flutter的key在widget list的作用以及必要性
  • nfs客户端进程变D,延伸linux的lock
  • 安卓应用性能调试和优化经验分享
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • ------- 计算机网络基础
  • 深入 Nginx 之配置篇
  • 听说你叫Java(二)–Servlet请求
  • 微信小程序--------语音识别(前端自己也能玩)
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 《天龙八部3D》Unity技术方案揭秘
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (007)XHTML文档之标题——h1~h6
  • (Git) gitignore基础使用
  • (Oracle)SQL优化技巧(一):分页查询
  • (八)c52学习之旅-中断实验
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (四)汇编语言——简单程序
  • (一一四)第九章编程练习
  • (转)Oracle存储过程编写经验和优化措施
  • (转)Scala的“=”符号简介
  • (转)原始图像数据和PDF中的图像数据
  • ***监测系统的构建(chkrootkit )
  • **CI中自动类加载的用法总结
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .Net Web窗口页属性
  • .NET 指南:抽象化实现的基类
  • .NET 中让 Task 支持带超时的异步等待
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .net下的富文本编辑器FCKeditor的配置方法
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(十)RCE (远程代码/命令执行漏洞)相关面试题