关闭

Java设计模式8:迭代器模式

标签: java设计模式迭代器迭代器模式
168人阅读 评论(0) 收藏 举报
分类:

迭代器模式

迭代器模式又叫做游标(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    4

这是因为恰好,我们知道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    6

看到这就遍历出来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、封装性好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心

缺点

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

0
0
查看评论

设计模式之迭代器模式java实现代码

迭代器模式:提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。 迭代器模式提供了一种方法,可以顺序访问一个聚合对象中的元素,而又不用知道内部是如何标示的。另外,迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象,让聚合更专注于数据集合。 案例实现情景:我和友人去超市分头购...
  • hailushijie
  • hailushijie
  • 2013-04-17 23:41
  • 1615

Java设计模式——迭代器模式

网上大部分人说迭代模式的时候,总是以某一种可遍历的对象为例进行介绍。这是可行的,这也是迭代模式的基本原型。当我看到《Head Frist设计模式》中迭代模式的时候,感觉要是能从另一个角度来说明,可能更能够体现迭代模式的威力所在。
  • u013761665
  • u013761665
  • 2016-03-04 12:39
  • 2388

Java程序员从笨鸟到菜鸟之(四十五)大话设计模式(九)迭代器模式和命令模式

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188    首先来看一下迭代器模式是干什么用的?       迭代这个名词对于熟悉Java的人来说绝对不陌生。我们常常使...
  • csh624366188
  • csh624366188
  • 2012-05-06 13:16
  • 6039

C++设计模式——迭代器模式

前言 又到年底了,时间真的过的好快啊。最近也非常感伤,总是怀念大学的日子,做梦的时候也常常梦到。梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业;建立一个链表,遍历链表,打印链表。现在把那个时候声明的链表的头文件拿出来看看: typedef struct tagNode ...
  • vipygd
  • vipygd
  • 2014-01-24 23:23
  • 5323

Java设计模式菜鸟系列(十一)迭代器模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39807741 迭代器模式(Iterator):提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。 迭代器模式就是顺序访问集合中的对象,这句话包含两层意思:一是需要遍...
  • JAVE_LOVER
  • JAVE_LOVER
  • 2014-10-06 14:28
  • 2115

C++设计模式之迭代器模式

前言 又到年底了,时间真的过的好快啊。最近也非常感伤,总是怀念大学的日子,做梦的时候也常常梦到。梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业;建立一个链表,遍历链表,打印链表。现在把那个时候声明的链表的头文件拿出来看看: 复制代码代码如下: typedef stru...
  • liujiayu2
  • liujiayu2
  • 2015-04-16 16:29
  • 480

HeadFirst 设计模式 9迭代器与组合模式(餐厅合并)

111
  • ysqjyjy
  • ysqjyjy
  • 2015-08-17 13:55
  • 491

Iterator(迭代器)设计模式

一、概念 提供一种方法访问一个容器对象中的各个元素,而又不暴露该对象的内部细节。 二、模式结构图
  • Sharing_Li
  • Sharing_Li
  • 2015-12-13 20:22
  • 473

Python设计模式(十六)【迭代器模式】

人只要不失去方向,就不会失去自己!""" Implementation of the iterator pattern with a generator 一个生成器实现迭代器模式 """ from __future__ import pri...
  • WuLex
  • WuLex
  • 2016-08-30 14:41
  • 1902

行为型:设计模式之迭代器模式(二十一)

20世纪80年代,那时我家有一台“古老的”电视机,牌子我忘了,只记得是台黑白电视机,没有遥控器,每次开关机或者换台都需要通过电视机上面的那些按钮来完成,我印象最深的是那个用来换台的按钮,需要亲自用手去旋转(还要使点劲才能拧动),每转一下就“啪”的响一声,如果没有收到任何电视频道就会出现一片让人眼花的...
  • z742182637
  • z742182637
  • 2015-12-25 13:57
  • 268
    个人资料
    • 访问:46343次
    • 积分:833
    • 等级:
    • 排名:千里之外
    • 原创:19篇
    • 转载:112篇
    • 译文:0篇
    • 评论:3条
    文章分类
    最新评论