设计模式解密(10)- 迭代器模式
1、简介
定义:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示;
主要用途:帮助我们遍历聚合对象;
关键代码:定义接口:hasNext, next;
英文:Iterator
类型:行为型模式
2、类图及组成
(引)类图:
组成:
抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法hasNext(),移出当前对象的方法remove(),
迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。
3、实例引入
模拟一下迭代器的简单实现:
package com.designpattern.iterator; /** * 抽象迭代器接口 * @author Json */ public interface Iterator { public boolean hasNext(); public Object next(); }
package com.designpattern.iterator; import java.util.ArrayList; /** * 具体迭代器实现 * @author Json */ public class ConcreteIterator implements Iterator { private ArrayList list = new ArrayList(); private int cursor = 0; public ConcreteIterator(ArrayList list) { this.list = list; } @Override public boolean hasNext() { if(cursor == list.size()){ return false; } return true; } @Override public Object next() { if(this.hasNext()){ return this.list.get(cursor++); } return null; } }
package com.designpattern.iterator; /** * 抽象容器 * @author Json */ public interface Aggregate { public void add(Object obj); public void remove(Object obj); public Iterator iterator(); }
package com.designpattern.iterator; import java.util.ArrayList; /** * 具体容器 * @author Json */ public class ConcreteAggregate implements Aggregate { private ArrayList list = new ArrayList(); @Override public void add(Object obj) { list.add(obj); } @Override public void remove(Object obj) { list.remove(obj); } @Override public Iterator iterator() { return new ConcreteIterator(list); } }
测试:
package com.designpattern.iterator; /** * 测试 * @author Json */ public class Test { public static void main(String[] args) { Aggregate agg = new ConcreteAggregate(); agg.add("Java"); agg.add("Ruby"); agg.add("Python"); Iterator iterator = agg.iterator(); while(iterator.hasNext()){ String s = (String) iterator.next(); System.out.println(s); } } }
结果:
Java
Ruby
Python
4、优缺点
优点:
1、它支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
5、应用场景
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。
3、为遍历不同的聚合结构提供一个统一的接口。
6、JDK的迭代器应用
JDK中就有大量迭代器的身影:
//java中迭代器接口源码: public interface Iterator<E> { boolean hasNext();//判断是否存在下一个对象元素 E next(); void remove(); }
迭代器模式与集合是紧密联系的,可以说密不可分,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像Java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的;
下面测试一下JDK迭代器的使用:
package com.designpattern.iterator.jdk_use_iterator; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 测试JDK自带的迭代器 * @author Json */ public class IteratorTest { public static void main(String[] args) { List list = new ArrayList(); for(int i = 0;i < 10;i++){ list.add("list_"+i); } Iterator iterList = list.iterator();//List接口实现了Iterable接口 while(iterList.hasNext()){ String str = (String)iterList.next(); System.out.print(str+"、"); } System.out.println(""); Map map = new HashMap(); for(int i = 0;i < 10;i++){ map.put(i,"map_"+i); } Iterator iterMap = map.entrySet().iterator(); while(iterMap.hasNext()){ Map.Entry strMap = (Map.Entry)iterMap.next(); System.out.print(strMap.getValue()+"、"); } } }
结果:
list_0、list_1、list_2、list_3、list_4、list_5、list_6、list_7、list_8、list_9、
map_0、map_1、map_2、map_3、map_4、map_5、map_6、map_7、map_8、map_9、
7、迭代器与foreach
从Java5起,Java中新增foreach循环,关于foreach的语法,可以实现两种数据类型的遍历,一个是数组,一个便是实现了Iterable接口的任何类,今天不讨论数组,我们主要关注实现了Iterable接口的类,是如何使用foreach遍历的;
foreach循环允许你在无需保持传统for循环中的索引,或在使用iterator时无需调用while循环中的hasNext()方法就能遍历Collection,foreach循环简化了任何Collection或array的遍历过程;
但是使用foreach循环也有两点需要注意:
1、 foreach循环通过iterator实现,使用foreach循环的对象必须实现Iterable接口;
foreach循环只适用于实现了Iterable<T>接口的对象。由于所有内置Collection类都实现了java.util.Collection接口,已经继承了Iterable;所以在使用foreach遍历Java集合,不用考虑iterator实现;
但是在以后涉及到自定义聚合对象时,要注意这一点;
2、 在遍历Collection时,如果要在遍历期间修改或删除Collection,则最好通过Iterator来实现,否则可能会发生“不确定的错误”。
附知识点: 在使用Java集合的时候,都需要使用 Iterator。但是java集合中还有一个迭代器ListIterator,在使用 List、ArrayList、LinkedList和 Vector的时候可以使用;
关于JAVA中 ListIterator和 Iterator区别 http://blog.csdn.net/longshengguoji/article/details/41551491
关于第二点测试例子:
package com.designpattern.iterator.jdk_use_iterator; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /** * * @author Json */ public class IteratorTest1 { public static void main(String[] args) { Collection<String> list = new ArrayList<String>(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); //removeItem1(list); //removeItem2(list); removeItem3(list); } /** * 错误用法1 */ public static void removeItem1(Collection<String> list){ Iterator<String> iterator_1 = list.iterator(); while (iterator_1.hasNext()) { String str = iterator_1.next(); if("张三".equals(str)){ list.remove(str); } } System.out.println(list); } /** * 错误用法2 */ public static void removeItem2(Collection<String> list){ for (String str : list) { if("张三".equals(str)){ list.remove(str); } } System.out.println(list); } /** * 正确用法 */ public static void removeItem3(Collection<String> list){ Iterator<String> iterator_3 = list.iterator(); while (iterator_3.hasNext()) { String str = iterator_3.next(); if("张三".equals(str)){ iterator_3.remove(); } } System.out.println(list); } }
8、总结
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据;
迭代器在平时需要我们自定义设计的情景不是很多,但是可能真会遇到这样的需求,所以有必要知道它是怎么实现的;
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 http://www.cnblogs.com/JsonShare/p/7196615.html