迭代器模式

迭代器模式又叫做游标(Cursor)模式,其作用是提供一种方法访问一个容器元素中的各个对象,而又不暴露该对象的内部细节

迭代器模式结构

迭代器模式由以下角色组成:

1、迭代器角色

负责定义访问和遍历元素的接口

2、具体迭代器角色

实现迭代器接口,并要记录遍历中的当前位置

3、容器角色

负责提供创建具体迭代器角色的接口

4、具体容器角色

实现创建具体迭代器角色的接口,这个具体迭代器角色与该容器的结构相关

迭代器模式在JDK中的应用及解读

迭代器模式就不自己写例子了,直接使用JDK中的例子。为什么我们要使用迭代器模式,思考一个问题,假如我有一个ArrayList和一个LinkedList:

List<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1);
arrayList.add(2); List<Integer> linkedList = new LinkedList<Integer>();
linkedList.add(3);
linkedList.add(4);

如何去遍历这两个List相信每个人都很清楚:

System.out.println("ArrayList:");
for (int i = 0; i < arrayList.size(); i++)
System.out.print(arrayList.get(i) + "\t"); System.out.println("\nLinkedList:");
for (int i = 0; i < linkedList.size(); i++)
System.out.print(linkedList.get(i) + "\t");

运行结果为:

ArrayList:
1 2
LinkedList:
3

这是因为恰好,我们知道ArrayList和LinkedList的访问方式,有些喜欢研究的人知道ArrayList和LinkedList的内部结构,但如果现在我给你一个HashSet:

HashSet<Integer> hashSet = new HashSet<Integer>();
hashSet.add(5);
hashSet.add(6);

将如何遍历?可能你还以为可以使用类似List的遍历方式,不过很遗憾,HashSet中根本没有提供get方法。

这时候就轮到迭代器出场了,不管是什么数据结构,不管你听过还是没听过,不管你见过还是没见过,只要它实现了Iterable接口,都可以用类似的方式去遍历,我把ArrayList、LinkedList、HashSet的遍历写在一起:

Iterator<Integer> iter = null;

System.out.println("ArrayList:");
iter = arrayList.iterator();
while (iter.hasNext())
{
System.out.print(iter.next() + "\t");
} System.out.println("\nLinkedList:");
iter = linkedList.iterator();
while (iter.hasNext())
{
System.out.print(iter.next() + "\t");
} System.out.println("\nHashSet:");
iter = hashSet.iterator();
while (iter.hasNext())
{
System.out.print(iter.next() + "\t");
}

看一下运行结果:

ArrayList:
1 2
LinkedList:
3 4
HashSet:
5

看到这就遍历出来ArrayList、LinkedList、HashSet了,以后遇到一个集合,只要实现了iterable接口,也都可以类似这么遍历。这就是开头迭代器模式的定义说的,开发者不需要知道集合中如何去遍历的细节,只管用类似的遍历方法就好了。

Iterable接口和Iterator接口

这两个都是迭代相关的接口,可以这么认为,实现了Iterable接口,则表示某个对象是可被迭代的;Iterator接口相当于是一个迭代器,实现了Iterator接口,等于具体定义了这个可被迭代的对象时如何进行迭代的。参看Iterable接口的定义:

public interface Iterable<T> {

    /**
* Returns an iterator over a set of elements of type T.
*
* @return an Iterator.
*/
Iterator<T> iterator();
}

这样对象就可以使用这个类的迭代器进行迭代了,一般Iterable和Iterator接口都是结合着一起使用的。为什么一定要实现Iterable接口而不直接实现Iterator接口了呢,这个问题我也是在自己写了ArrayList和LinkedList的实现之后才想明白的,这么做确实有道理:

因为Iterator接口的核心方法next()或者hasNext()都是依赖于迭代器的当前迭代位置的。如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据,当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知的。除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。但即使这样,Collection也同时只能存在一个当前迭代位置。而Iterable,每次调用都返回一个从头开始计数的迭代器,多个迭代器时互不干扰。

可能这么解释不是很明白,再解释明白一点,我自己写的一个ArrayList,如果直接实现Iterator接口,那么势必是这么写的:

public class ArrayList<E> implements List<E>, Iterator<E>, RandomAccess, Cloneable, Serializable
{
/**
* 序列化ID
*/
private static final long serialVersionUID = -5786598508477165970L; private int size = 0;
private transient Object[] elementData = null; public E next()
{
...
} public boolean hasNext()
{
...
}
...
}

这么问题就来了,如果一个ArrayList实例被多个地方迭代,next()方法、hasNext()直接操作的是ArrayList中的资源,假如我在ArrayList中定义一个迭代位置的变量,那么对于不同调用处,这个迭代变量是共享的,线程A迭代的时候将迭代变量设置成了第5个位置,这时候切换到了线程B,对于线程B来讲,就从第5个位置开始遍历此ArrayList了,根本不是从0开始,如何正确迭代?

实现Iterable接口返回一个Iterator接口的实例就不一样了,我为自己写的ArrayList定义一个内部类:

public class ArrayListIterator implements Iterator<E>
{
int iteratorPostion = 0; /**
* 判断是否后面还有元素
*/
@Override
public boolean hasNext()
{
if ((iteratorPostion + 1) > size)
return false;
return true;
} /**
* 返回之前一个元素的引用
*/
@Override
public E next()
{
return (E)elementData[iteratorPostion++];
}
...
}

每次都返回一个返回一个ArrayListIterator实例出去:

/**
* 返回一个ArrayList的迭代器,可以通过该迭代器遍历ArrayList中的元素
*/
public Iterator<E> iterator()
{
return new ArrayListIterator();
}

这就保证了,即使是多处同时迭代这个ArrayList,依然每处都是从0开始迭代这个ArrayList实例的。

迭代器模式的优缺点

优点

1、简化了便利方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们还可以通过下标来获取,但用户需要在对集合很了解的情况下,才能自行遍历对象(有时即使你了解了集合,还未必能直接遍历,比如上面的HashSet就没有提供get方法)。而引入了迭代器方法后,用户用起来就简单地多了

2、可以供多种遍历方式,比如对于有序列表,可以正向遍历也可以倒序遍历,只要迭代器实现得好

3、封装性好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心

缺点

对于比较简单的遍历(数组或者有序列表),使用迭代器方式遍历较为繁琐而且遍历效率不高,使用迭代器的方式比较适合那些底层以链表形式实现的集合

Java设计模式8:迭代器模式的更多相关文章

  1. 折腾Java设计模式之迭代器模式

    迭代器模式 Provide a way to access the elements of an aggregate object sequentially without exposing its ...

  2. 16.java设计模式之迭代器模式

    基本需求: 展示一个学校的结构,比如一个学校下面有多个学院,学院下面有多个系,对其节点主要是遍历,与组合模式略有不同 传统方案: 学校<-学院<-系 依次继承 这种方式,在一个页面中展示出 ...

  3. 简单的了解下Java设计模式:迭代器模式(转载)

    迭代器模式定义 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. Java 开发过程中遍历是常用的.如下边程序: for(int i =0 ; ...

  4. java设计模式之迭代器模式

    一.迭代器模式简介 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露内部的表示.把游走的任务放在迭代器上,而不是 ...

  5. Python进阶:设计模式之迭代器模式

    在软件开发领域中,人们经常会用到这一个概念——“设计模式”(design pattern),它是一种针对软件设计的共性问题而提出的解决方案.在一本圣经级的书籍<设计模式:可复用面向对象软件的基础 ...

  6. 设计模式学习--迭代器模式(Iterator Pattern)和组合模式(Composite Pattern)

    设计模式学习--迭代器模式(Iterator Pattern) 概述 ——————————————————————————————————————————————————— 迭代器模式提供一种方法顺序 ...

  7. 实践GoF的设计模式:迭代器模式

    摘要:迭代器模式主要用在访问对象集合的场景,能够向客户端隐藏集合的实现细节. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:迭代器模式>,作者:元闰子. 简介 有时会遇到这 ...

  8. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  9. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  10. 乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern)

    原文:乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern) 作者:weba ...

随机推荐

  1. 详解C语言的类型转换

    1.自动类型转换 字符型变量的值实质上是一个8位的整数值,因此取值范围一般是-128-127,char型变量也可以加修饰符unsigned,则unsigned char 型变量的取值范围是0-255( ...

  2. poj1014(还需要改动)

    #include <stdio.h> int n[6]; int main() { freopen("in.txt","r",stdin); int ...

  3. CodeForces 743C Vladik and fractions (数论)

    题意:给定n,求三个不同的数满足,2/n = 1/x + 1/y + 1/z. 析:首先1是没有解的,然后其他解都可以这样来表示 1/n, 1/(n+1), 1/(n*(n+1)),这三个解. 代码如 ...

  4. JAVA里面的IO流(一)分类1(字节/字符和输入/输出)

      java.io包中定义了多个流类型(流或抽象类)来实现输入/输出功能:可以从不同的角度对其进行分类: 按数据流的方向不同可以分为输入流和输出流 从文件读数据为输入流:往文件写数据为输出流 按处理数 ...

  5. 2016-11-15mysql优化笔记

    1.mysql连接数:MYSQL数据库安装完成后,默认最大连接数是100,一般流量稍微大一点的论坛或网站这个连接数是远远不够的,连接数少的话,在大并发下连接数会不够用,会有很多线程在等待其他连接释放, ...

  6. MongoDB学习笔记-05 聚合

    MongoDB除了基本查询功能之外,还有强大的聚合工具,其中包括:count().distinct().group().mapreduce. 计数函数count count是最简单的聚合工具,用于返回 ...

  7. Chrome: Resource interpreted as Font but transferred with MIME type font/x-woff

    最近,项目中加入了Bootstrap进行界面优化,但是,项目加载运行之后,控制台总是提示以下错误信息: GET http://localhost:8080/.../fonts/fontawesome- ...

  8. [Leetcode][JAVA] Recover Binary Search Tree (Morris Inorder Traversal)

    Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...

  9. 剑指offer算法_位运算求和

    不用+,-,*,/运算求和,可以分成三步: 1.计算两个数字的异或值,相当于只计算每一位的和,不计算进位,得出结果sum: 2.计算两个数字的与值,相当于求出两个数字的进位,然后左移一位,相当于进位, ...

  10. Java实现四则运算,使用堆栈,检查语法

    突然发闲想试一试自己实现算术的四则运算,支持加减乘除和括号.正负号:支持语法检查:思路很常规,利用两个堆栈,一个压操作符,一个压操作数,念头冒出来之后,立马动手:然后本以为很容易的一个实现,却存在各种 ...