美文网首页程序员
Iterator和Iterable解析

Iterator和Iterable解析

作者: pjmike | 来源:发表于2017-10-16 16:48 被阅读0次

    之前一直搞不清楚Iterator和Iterable到底有什么关系,它们的区别是什么,今天查看Java集合类源码才发现其中的名堂,接下来给大家讲讲我的分析与理解。

    Iterable接口

    Iterable是接口,Iterable是1.5引入的新特性,Iterator是1.2就有了,二者都是为了迭代造作,Iterable只是包装了Iterator,从而允许实现此接口的对象成为foreach语句的目标,而且这样的话,更方便以后的扩展。

    public interface Iterable<T> {
        /**
         * Returns an iterator over elements of type {@code T}.
         *
         * @return an Iterator.
         */
        Iterator<T> iterator();
       
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    
        default Spliterator<T> spliterator() {
            return Spliterators.spliteratorUnknownSize(iterator(), 0);
        }
    }
    

    Java集合Collection接口就扩展了Iterable接口。实现Iterable接口的那些类就可以拥有增强的for循环,该循环施于这些类之上以观察他们所有的项。

    
    public interface Collection<E> extends Iterable<E> {
        
        int size();
        boolean isEmpty();
        boolean contains(Object o);
        Iterator<E> iterator();
        Object[] toArray();
        boolean add(E e);
        boolean remove(Object o);
        boolean containsAll(Collection<?> c);
        boolean removeAll(Collection<?> c);
        default boolean removeIf(Predicate<? super E> filter) {
            Objects.requireNonNull(filter);
            boolean removed = false;
            final Iterator<E> each = iterator();
            while (each.hasNext()) {
                if (filter.test(each.next())) {
                    each.remove();
                    removed = true;
                }
            }
            return removed;
        }
        boolean retainAll(Collection<?> c);
        void clear();
        boolean equals(Object o);
        int hashCode();
        @Override
        default Spliterator<E> spliterator() {
            return Spliterators.spliterator(this, 0);
        }
        default Stream<E> parallelStream() {
            return StreamSupport.stream(spliterator(), true);
        }
    }
    

    实现Iterable接口的集合必须提供一个称为itrator的方法,该方法又返回一个Iterator类型的对象。所以例如List集合类,Set类,他们实现了Collection接口,自然实现了Iterable接口,在其源码中有具体的iterator方法,例如:ArraryList类中,它实现了List接口

        public Iterator<E> iterator() {
            return new 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();
                }
            }
            ...
    

    Iterator迭代器

    Iterator接口的思路是,通过iterator方法,每个集合均可创建并返回给客户一个实现Iterator接口的对象。

    public interface Iterator<E> {
        boolean hasNext();
        T next();
        void remove();
    }
    

    Iterator一般用于简单遍历集合中的元素。而Iterator还有一个有用的方法叫做remove()方法,相对于Collection中的remove而言,它具有更多的优点。

    对比Collection中remove方法和Iterator中remove方法

    • 抽象类AbstractCollection(实现了Collection接口)的remove方法
    public boolean remove(Object o) {
            Iterator<E> it = iterator();
            if (o==null) {
                while (it.hasNext()) {
                    if (it.next()==null) {
                        it.remove();
                        return true;
                    }
                }
            } else {
                while (it.hasNext()) {
                    //找出要被删除的项
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }
    

    Collection的remove方法必须首先找出要被删除的项,开销较大。

    • Iterator中的remove方法
     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 void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);//remove()方法下面有给出
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
        }
     }
    
    

    以上是ArrayList中其实现Iterator接口的内部类中remove方法,可能其中有些变量你看不懂,需要联系整个源码才能够明白它大体的意思。

    以下是ArrayList重写AbstractList中remove()方法。

    public E remove(int index) {
    
            rangeCheck(index);//检查是否越界
    
            modCount++;
            E oldValue = elementData(index);//得到要删除的元素
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);//复制数组
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;//返回要删除的元素
        }
    

    不难看出Iterator的remove方法并没有一个一个比较找出所要删除的项,而是知道要删除项的准确位置,那么删除它的开销就小很多。


    使用Iterator的基本法则

    如果对正在被迭代的集合进行结构上的改变(即对集合使用add,remove或clear方法),那么迭代器就不再合法(并且在其后使用该迭代器时将会有ConcurrentModificationException异常被抛出)。然而,如果迭代器调用了它自己的remove方法,那这个迭代器仍然是合法的,所以这是我们有时候更愿意使用迭代器的remove方法的第二个原因。

    相关文章

      网友评论

        本文标题:Iterator和Iterable解析

        本文链接:https://www.haomeiwen.com/subject/imfluxtx.html