Iterator和Iterable解析


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

Iterable接口

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

public interface Iterable {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator iterator();
   
    default void forEach(Consumer action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

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


public interface Collection extends Iterable {
    
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator iterator();
    Object[] toArray();
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection c);
    boolean removeAll(Collection c);
    default boolean removeIf(Predicate filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator 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 spliterator() {
        return Spliterators.spliterator(this, 0);
    }
    default Stream parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

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

    public Iterator iterator() {
        return new Itr();
    }
    private class Itr implements Iterator {
        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 {
    boolean hasNext();
    T next();
    void remove();
}

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

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

  • 抽象类AbstractCollection(实现了Collection接口)的remove方法
public boolean remove(Object o) {
        Iterator 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 {
        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解析)