设计模式:迭代器模式

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

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

迭代器的结构

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


// 接口定义方式一
public interface Iterator<E> {
  boolean hasNext();
  void next();
  E currentItem();
}

// 接口定义方式二
public interface Iterator<E> {
  boolean hasNext();
  E next();
}

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


public interface List<E> {
  Iterator iterator();
  //...省略其他接口函数...
}

public class ArrayList<E> implements List<E> {
  //...
  public Iterator iterator() {
    return new ArrayIterator(this);
  }
  //...省略其他代码
}

public class Demo {
  public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("xzg");
    names.add("wang");
    names.add("zheng");
    
    Iterator<String> iterator = names.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.currentItem());
      iterator.next();
    }
  }
}

在这里插入图片描述

为什么要用迭代器模式

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

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

职责单一,用来解耦

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

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

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

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

迭代器的引入

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


List<String> 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<String> iterator = names.iterator();
while (iterator.hasNext()) {
  System.out.print(iterator.next() + ",");//Java中的迭代器接口是第二种定义方式,next()既移动游标又返回数据
}

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

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

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值