• Iterator 和 Iterable 差别和联系


    用Iterator模式实现遍历集合 

            Iterator模式是用于遍历集合类的标准訪问方法。它能够把訪问逻辑从不同类型的集合类中抽象出来,从而避免向client暴露集合的内部结构。

            比如,假设没有使用Iterator,遍历一个数组的方法是使用索引: for(int i=0; i<array.size(); i++) { ... get(i) ... }

            而訪问一个链表(LinkedList)又必须使用while循环: while((e=e.next())!=null) { ... e.data() ... } 

            以上两种方法client都必须事先知道集合的内部结构,訪问代码和集合本身是紧耦合。无法将訪问逻辑从集合类和client代码中分离出来,每一种集合相应一种遍历方法,client代码无法复用。

            更恐怖的是,假设以后须要把ArrayList更换为LinkedList,则原来的client代码必须所有重写。

            解决以上问题。Iterator模式总是用同一种逻辑来遍历集合: for(Iterator it = c.iterater(); it.hasNext(); ) { ... } 

            奥秘在于client自身不维护遍历集合的"指针"。全部的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成。因此,它知道怎样遍历整个集合。

            client从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后"。"取当前元素"的命令,就能够间接遍历整个集合。 

            首先看看java.util.Iterator接口的定义:
                    public interface Iterator { boolean hasNext(); Object next(); void remove(); } 

            依赖前两个方法就能完毕遍历,典型的代码例如以下:
                    for(Iterator it = c.iterator(); it.hasNext(); ) { Object o = it.next(); // 对o的操作... } 

            每一种集合类返回的Iterator详细类型可能不同,Array可能返回ArrayIterator,Set可能返回 SetIterator。Tree可能返回TreeIterator,可是它们都实现了Iterator接口。因此,client不关心究竟是哪种 Iterator,它仅仅须要获得这个Iterator接口就可以。这就是面向对象的威力。 

            全部集合类都实现了 Collection 接口,而 Collection 继承了 Iterable 接口。

    /**
     * Implementing this interface allows an object to be the target of
     * the "foreach" statement.
     *
     * @param <T> the type of elements returned by the iterator
     *
     * @since 1.5
     */
    public interface Iterable<T> {
    
        /**
         * Returns an iterator over a set of elements of type T.
         *
         * @return an Iterator.
         */
        Iterator<T> iterator();
    }

            而在详细的实现类中(比方 ArrayList),则在内部维护了一个 Itr 内部类。该类继承了 Iterator 接口,它的hasNext() 和 next() 方法是和 ArrayList 实现相耦合的。

    当调用 ArrayList 对象的 iterator() 方法的时候,返回该类 Itr 的一个实例,从而实现遍历 ArrayList 的功能。

    public Iterator<E> iterator() {
            return new Itr();
        }
    
        /**
         * An optimized version of AbstractList.Itr
         */
        private class Itr implements Iterator<E> {
            int cursor;       // index of next element to return
            int lastRet = -1; // index of last element returned; -1 if no such
            int expectedModCount = modCount;
    
            public boolean hasNext() {
                return cursor != size;
            }
    
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification();
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[lastRet = i];
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

    为什么一定要去实现Iterable这个接口呢?为什么不直接实现Iterator接口呢?

            看一下JDK中的集合类,比方List一族或者Set一族,都是实现了Iterable接口。但并不直接实现Iterator接口。 细致想一下这么做是有道理的。

            由于Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。

    假设Collection直接实现Iterator接口。势必导致集合对象中包括当前迭代位置的数据(指针)。 当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。

    除非再为Iterator接口加入一个reset()方法,用来重置当前迭代位置。 但即时这样,Collection也仅仅能同一时候存在一个当前迭代位置

    而Iterable则不然,每次调用都会返回一个从头開始计数的迭代器。 多个迭代器是互不干扰的。


  • 相关阅读:
    main函数的一些特性
    确保函数的操作不超出数组实参的边界
    今天学习了一点sed
    libevent 与事件驱动
    mvc3 action验证失败后的自定义处理
    使用spring.net+nibernate时如何用aspnet_regiis加密数据库连接字符串
    C# 中 IList IEnumable 转换成 List类型
    Nhibernate 过长的字符串报错 dehydration property
    小论接口(interface)和抽象类(abstract class)的区别
    C# 语言在函数参数列表中出现this关键词的作用
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5114304.html
Copyright © 2020-2023  润新知