zoukankan      html  css  js  c++  java
  • Java中的迭代器模式

    迭代器模式

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

    迭代器模式的结构

    1、迭代器角色

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

    2、具体迭代器角色

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

    3、容器角色

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

    4、具体容器角色

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

    为什么需要迭代器模式

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

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

    运行结果:

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

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

    Java中的Iterable和Iterator

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

    1 public interface Iterable<T> {
    2 
    3     /**
    4      * Returns an iterator over a set of elements of type T.
    5      * 
    6      * @return an Iterator.
    7      */
    8     Iterator<T> iterator();
    9 }
     1 public interface Iterator<E> {
     2     /**
     3      * Returns {@code true} if the iteration has more elements.
     4      * (In other words, returns {@code true} if {@link #next} would
     5      * return an element rather than throwing an exception.)
     6      *
     7      * @return {@code true} if the iteration has more elements
     8      */
     9     boolean hasNext();
    10 
    11     /**
    12      * Returns the next element in the iteration.
    13      *
    14      * @return the next element in the iteration
    15      * @throws NoSuchElementException if the iteration has no more elements
    16      */
    17     E next();
    18 
    19     /**
    20      * Removes from the underlying collection the last element returned
    21      * by this iterator (optional operation).  This method can be called
    22      * only once per call to {@link #next}.  The behavior of an iterator
    23      * is unspecified if the underlying collection is modified while the
    24      * iteration is in progress in any way other than by calling this
    25      * method.
    26      *
    27      * @implSpec
    28      * The default implementation throws an instance of
    29      * {@link UnsupportedOperationException} and performs no other action.
    30      *
    31      * @throws UnsupportedOperationException if the {@code remove}
    32      *         operation is not supported by this iterator
    33      *
    34      * @throws IllegalStateException if the {@code next} method has not
    35      *         yet been called, or the {@code remove} method has already
    36      *         been called after the last call to the {@code next}
    37      *         method
    38      */
    39     default void remove() {
    40         throw new UnsupportedOperationException("remove");
    41     }
    42 
    43     /**
    44      * Performs the given action for each remaining element until all elements
    45      * have been processed or the action throws an exception.  Actions are
    46      * performed in the order of iteration, if that order is specified.
    47      * Exceptions thrown by the action are relayed to the caller.
    48      *
    49      * @implSpec
    50      * <p>The default implementation behaves as if:
    51      * <pre>{@code
    52      *     while (hasNext())
    53      *         action.accept(next());
    54      * }</pre>
    55      *
    56      * @param action The action to be performed for each element
    57      * @throws NullPointerException if the specified action is null
    58      * @since 1.8
    59      */
    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<E> implements List<E>, Iterator<E>, RandomAccess, Cloneable, Serializable
     2 {
     3     /**
     4      * 序列化ID
     5      */
     6     private static final long serialVersionUID = -5786598508477165970L;
     7     
     8     private int size = 0;
     9     private transient Object[] elementData = null;
    10     
    11     public E next()
    12     {
    13         ...
    14     }
    15     
    16     public boolean hasNext()
    17     {
    18         ...
    19     }
    20     ...
    21 }

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

     1 public class ArrayListIterator implements Iterator<E>
     2 {
     3     int iteratorPostion = 0;
     4     
     5     /**
     6      * 判断是否后面还有元素
     7      */
     8     @Override
     9     public boolean hasNext()
    10     {
    11         if ((iteratorPostion + 1) > size)
    12             return false;
    13         return true;
    14     }
    15 
    16     /**
    17      * 返回之前一个元素的引用
    18      */
    19     @Override
    20     public E next()
    21     {
    22         return (E)elementData[iteratorPostion++];
    23     }
    24     ...
    25 } 

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

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

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

    迭代器模式的优缺点

      迭代器模式的优点:

    • 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
    • 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
    • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。

      迭代器模式的缺点:

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

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

     

  • 相关阅读:
    python内置模块collections介绍
    Python的set集合详解
    不同 Python 数据类型的搜寻
    Python 分支、循环、条件与枚举
    ssrf爆破mysql
    php反序列化
    thinkphp历史漏洞
    Thinkphp 缓存RCE
    绕WAF文章收集
    mssql手工盲注
  • 原文地址:https://www.cnblogs.com/sunl123/p/11100199.html
Copyright © 2011-2022 走看看