java 迭代模式_Java中的迭代器模式

迭代器模式

提供一种方式去访问一个容器元素中的各个对象,而又不暴露该对象的内部细节。

迭代器模式的结构

1、迭代器角色

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

2、具体迭代器角色

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

3、容器角色

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

4、具体容器角色

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

为什么需要迭代器模式

列举一个简单的示例,遍历ArrayList、LinkedList、HashSet中各个元素:

1 public static voidmain(String[] args) {2 List arrayList = new ArrayList();3 arrayList.add(1);4 arrayList.add(2);5 List linkedList = new LinkedList();6 linkedList.add(3);7 linkedList.add(4);8 HashSet hashSet = new HashSet();9 hashSet.add(5);10 hashSet.add(6);11 Iterator iterator = null;12 iterator =arrayList.iterator();13 System.out.println("ArrayList:");14 while(iterator.hasNext())15 {16 System.out.print(iterator.next() + "\t");17 }18 System.out.println("\nLinkedList:");19 iterator =linkedList.iterator();20 while(iterator.hasNext())21 {22 System.out.print(iterator.next() + "\t");23 }24 System.out.println("\nHashSet:");25 iterator =hashSet.iterator();26 while(iterator.hasNext())27 {28 System.out.print(iterator.next() + "\t");29 }30 }

运行结果:

ArrayList:1 2LinkedList:3 4HashSet:5 6

随便一个结合,是要实现了iterable接口,就可以用这样的方式遍历。开发者不需要知道集合中如何去遍历的细节,只管用类似的遍历方法就好了。

Java中的Iterable和Iterator

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

1 public interface Iterable{2

3 /**

4 * Returns an iterator over a set of elements of type T.5 *6 *@returnan Iterator.7 */

8 Iteratoriterator();9 }

1 public interface Iterator{2 /**

3 * Returns {@codetrue} if the iteration has more elements.4 * (In other words, returns {@codetrue} if {@link#next} would5 * return an element rather than throwing an exception.)6 *7 *@return{@codetrue} if the iteration has more elements8 */

9 booleanhasNext();10

11 /**

12 * Returns the next element in the iteration.13 *14 *@returnthe next element in the iteration15 *@throwsNoSuchElementException if the iteration has no more elements16 */

17 E next();18

19 /**

20 * Removes from the underlying collection the last element returned21 * by this iterator (optional operation). This method can be called22 * only once per call to {@link#next}. The behavior of an iterator23 * is unspecified if the underlying collection is modified while the24 * iteration is in progress in any way other than by calling this25 * method.26 *27 * @implSpec28 * The default implementation throws an instance of29 * {@linkUnsupportedOperationException} and performs no other action.30 *31 *@throwsUnsupportedOperationException if the {@coderemove}32 * operation is not supported by this iterator33 *34 *@throwsIllegalStateException if the {@codenext} method has not35 * yet been called, or the {@coderemove} method has already36 * been called after the last call to the {@codenext}37 * method38 */

39 default voidremove() {40 throw new UnsupportedOperationException("remove");41 }42

43 /**

44 * Performs the given action for each remaining element until all elements45 * have been processed or the action throws an exception. Actions are46 * performed in the order of iteration, if that order is specified.47 * Exceptions thrown by the action are relayed to the caller.48 *49 * @implSpec50 *

The default implementation behaves as if:51 *

{@code

52 * while (hasNext())53 * action.accept(next());54 * }

55 *56 *@paramaction The action to be performed for each element57 *@throwsNullPointerException if the specified action is null58 *@since1.859 */

60 default void forEachRemaining(Consumer super E>action) {61 Objects.requireNonNull(action);62 while(hasNext())63 action.accept(next());64 }65 }

为什么一定要实现Iterable接口而不是直接实现Iterator接口?

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

1 public class ArrayList implements List, Iterator, RandomAccess, Cloneable, Serializable2 {3 /**

4 * 序列化ID5 */

6 private static final long serialVersionUID = -5786598508477165970L;7

8 private int size = 0;9 private transient Object[] elementData = null;10

11 publicE next()12 {13 ...14 }15

16 public booleanhasNext()17 {18 ...19 }20 ...21 }

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

1 public class ArrayListIterator implements Iterator

2 {3 int iteratorPostion = 0;4

5 /**

6 * 判断是否后面还有元素7 */

8 @Override9 public booleanhasNext()10 {11 if ((iteratorPostion + 1) >size)12 return false;13 return true;14 }15

16 /**

17 * 返回之前一个元素的引用18 */

19 @Override20 publicE next()21 {22 return (E)elementData[iteratorPostion++];23 }24 ...25 }

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

1 /**

2 * 返回一个ArrayList的迭代器,可以通过该迭代器遍历ArrayList中的元素3 */

4 public Iteratoriterator()5 {6 return newArrayListIterator();7 }

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

迭代器模式的优缺点

迭代器模式的优点:

简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。

可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。

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

迭代器模式的缺点:

对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。

总的来说:迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值