• 设计模式:迭代器模式


    迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern)

    这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。 迭代器模式属于行为型模式。

    迭代器的结构

    迭代器模式组成部分:容器+容器迭代器
    为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类。
    在这里插入图片描述
    迭代器接口定义

    
    // 接口定义方式一
    public interface Iterator {
      boolean hasNext();
      void next();
      E currentItem();
    }
    
    // 接口定义方式二
    public interface Iterator {
      boolean hasNext();
      E next();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里面我们拿ArrayListl来举例子(其实这个例子不太好)
    ArrayList是容器,他的迭代器是ListIterator或者Iterator,这个就是一个迭代器接口,只不过ArrayList是一个类似模板模式继承过来的实践类,里面的一些迭代方法实际是在Array List里面实现的。
    在这里插入图片描述

    
    public interface List {
      Iterator iterator();
      //...省略其他接口函数...
    }
    
    public class ArrayList implements List {
      //...
      public Iterator iterator() {
        return new ArrayIterator(this);
      }
      //...省略其他代码
    }
    
    public class Demo {
      public static void main(String[] args) {
        List names = new ArrayList<>();
        names.add("xzg");
        names.add("wang");
        names.add("zheng");
        
        Iterator iterator = names.iterator();
        while (iterator.hasNext()) {
          System.out.println(iterator.currentItem());
          iterator.next();
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    为什么要用迭代器模式

    我只想简单的遍历,不想太麻烦。

    这个迭代器模式其实用的地方其实主要是针对的是容器的遍历,简单的数据结构数组遍历很简单直接,但是一旦设计到复杂点,比如说哈希散列的HashMap,或者是数据结构考试总喜欢考察树的遍历,是左遍历还是有便利好几种方式。我们不需要具体数据结构或者容器里面的实现细节。你怎么散列的我不想知道,你从哪个方向开始遍历,我都不想知道,我都吃上猪肉,没必要研究猪是怎么跑的。

    职责单一,用来解耦

    就是容器都有自己的迭代器来处理,我修改迭代方式影响更小,而不是堆在一块。

    它用来遍历集合对象。这里说的“集合对象”也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如数组、链表、树、图、跳表。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。

    扩展添加更让容易,开闭原则

    迭代器模式让添加新的遍历算法更加容易,更符合开闭原则。除此之外,因为迭代器都实现自相同的接口,在开发中,基于接口而非实现编程,替换迭代器也变得更加容易

    迭代器的引入

    三种遍历:for 循环、foreach 循环、iterator 迭代器
    foreach是基于迭代器的语法糖

    
    List names = new ArrayList<>();
    names.add("xzg");
    names.add("wang");
    names.add("zheng");
    
    // 第一种遍历方式: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 iterator = names.iterator();
    while (iterator.hasNext()) {
      System.out.print(iterator.next() + ",");//Java中的迭代器接口是第二种定义方式,next()既移动游标又返回数据
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    迭代器为什么不支持遍历过程中增删,如果发生了怎么处理?
    不管事迭代器还是别的遍历方式,我们一般在便利的过程中,对于删除新增都是要注意的,因为改变了结构,好比正在盘点呢,你这边还在不停的入库和出库,这个盘点结果就不一定是准确的。不过,并不是所有情况下都会遍历出错,有的时候也可以正常遍历,所以,这种行为称为结果不可预期行为或者未决行为,也就是说,运行结果到底是对还是错,要视情况而定。

    当通过迭代器来遍历集合的时候,增加、删除集合元素会导致不可预期的遍历结果。实际上,“不可预期”比直接出错更加可怕,有的时候运行正确,有的时候运行错误,一些隐藏很深、很难
    debug 的 bug 就是这么产生的。那我们如何才能避免出现这种不可预期的运行结果呢?

    怎么解决不可预期的行为
    两种方式:一是 遍历的时候不让增加修改 二是我遍历的时候,有新增删除直接报错。

    第一种比较死板,而且我也不知道什么时候结束,这就可能后面操作不知道怎么搞。
    第二种解决方法更加合理。Java 语言就是采用的这种解决方案,增删元素之后,让遍历报错。

    如何实现一个支持“快照”功能的迭代器模式
    方案一 定义一个snapshot的快照变量来专门存这些
    方案二 不真删除 而是为容器元素添加创建时间戳和删除时间戳,同样迭代器也有一个时间戳

    我们可以在容器中,为每个元素保存两个时间戳,一个是添加时间戳 addTimestamp,一个是删除时间戳
    delTimestamp。当元素被加入到集合中的时候,我们将 addTimestamp 设置为当前时间,将 delTimestamp
    设置成最大长整型值(Long.MAX_VALUE)。当元素被删除时,我们将 delTimestamp
    更新为当前时间,表示已经被删除。注意,这里只是标记删除,而非真正将它从容器中删除。同时,每个迭代器也保存一个迭代器创建时间戳
    snapshotTimestamp,也就是迭代器对应的快照的创建时间戳。当使用迭代器来遍历容器的时候,只有满足
    addTimestamp addTimestamp>snapshotTimestamp,说明元素在创建了迭代器之后才加入的,不属于这个迭代器的快照;如果元素的
    delTimestamp

  • 相关阅读:
    Jenkins(4)超时机制、运行命令(shell)、自动构建 和 邮件通知
    【SQLServer】max worker threads参数配置
    浅谈c++中的关键字----const
    LeetCode:200.岛屿数量
    MacOS 文件句柄数不够 Error: EMFILE: too many open files
    搭建网站使用宝塔面板遇到的问题
    C语言 -- 操作符详解
    AUTOSAR基础篇之StbM
    python趣味编程-恐龙克隆游戏
    linux之shell脚本练习
  • 原文地址:https://blog.csdn.net/FeiChangWuRao/article/details/126865650