迭代器模式及典型应用

本文的主要内容:

介绍迭代器模式

源码分析迭代器模式的典型应用

Java集合中的迭代器模式

Mybatis中的迭代器模式

迭代器模式

迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。

角色

Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。

ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。

Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。

ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。

在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。

在迭代器模式中应用了工厂方法模式,抽象迭代器对应于抽象产品角色,具体迭代器对应于具体产品角色,抽象聚合类对应于抽象工厂角色,具体聚合类对应于具体工厂角色。

示例

我们来实现一个学生报数的示例

定义一个学生类,有一个报数方法

定义班级接口和班级类

定义迭代器接口并实现迭代器

测试,进行报数

输出

迭代器模式类图如下

示例.迭代器类图

迭代器模式总结

迭代器模式的主要优点如下:

它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。

迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。

在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足 "开闭原则" 的要求。

迭代器模式的主要缺点如下:

由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

适用场景:

访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。

需要为一个聚合对象提供多种遍历方式。

为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。

源码分析迭代器模式的典型应用

Java集合中的迭代器模式

看 类

从 源码中看到了有两个迭代器 和 ,分别实现 和 接口;

第一个当然很容易看明白,它跟我们示例的迭代器的区别是这里是一个内部类,可以直接使用 的数据列表;第二个迭代器是第一次见到, 跟 有什么区别呢?

先看 源码

接着是 的源码

通过源码我们看出: 是一个功能更加强大的迭代器,它继承于 接口,只能用于各种List类型的访问。可以通过调用 方法产生一个指向List开始处的 , 还可以调用 方法创建一个一开始就指向列表索引为n的元素处的 。

和 主要区别概括如下:

有 方法,可以向List中添加对象,而 不能

和 都有 和 方法,可以实现顺序向后遍历,但是 有 和 方法,可以实现逆向(顺序向前)遍历。 就不可以。

可以定位当前的索引位置, 和 可以实现。 没有此功能。

都可实现删除对象,但是 可以实现对象的修改, 方法可以实现。 仅能遍历,不能修改。

敲一个 Iterator 的 Demo 探究一下

输出结果

可以看到 会删除原来的 List 对象的数据

再敲一个 ListIterator 的 Demo 探究一下

结果如下

可以看出 的 、、 方法会直接改变原来的 List 对象,而且可以通过 反向遍历

Mybatis中的迭代器模式

当查询数据库返回大量的数据项时可以使用游标 ,利用其中的迭代器可以懒加载数据,避免因为一次性加载所有数据导致内存奔溃,Mybatis 为 接口提供了一个默认实现类 ,代码如下

游标迭代器 实现了 迭代器接口,这里的迭代器模式跟 中的迭代器几乎一样

参考:

刘伟:设计模式Java版

慕课网java设计模式精讲 Debug 方式+内存分析

Java 集合中关于Iterator 和ListIterator的详解

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181012A0BECV00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券