Java迭代器

迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。

迭代器作为一种设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而又无需暴露该对象的内部实现,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

Java中的Iterator一般称为“轻量级”对象,创建它的代价是比较小的。这里不会去考究迭代器这种设计模式,仅在JDK代码层面上谈谈迭代器的时候以及使用迭代器的好处。

package java.util;
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

Iterator是作为一个接口存在的,它定义了迭代器所具有的功能。对于这三个方法所实现的功能,字面意义就是了。hasNext()方法可以判断对象中是否还存在元素,next()方法可以获取当前元素并移动到下一个元素位置,remove()方法可以删除当前元素。

我们以ArrayList类为例,看看这三个方法如何实现。

/**
 * Returns an iterator over the elements in this list in proper sequence.
 *
 * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
 *
 * @return an iterator over the elements in this list in proper sequence
 */
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;

    Itr() {}

    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();
    }
}

对于上述的代码不难看懂,有点疑惑的是int expectedModCount = modCount;这句代码,其实这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。阅读源码就可以知道ArrayList中存在modCount对象,增删操作都会使modCount++,通过两者的对比迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!

以一个实际的例子来看:

Iterator<Character> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
    list.add('o');
}

运行结果为:

JavaScript迭代方法 迭代器 java_迭代

仔细观察上述的各个方法,我们在源码中就会发现一个特别的属性modCount,API解释如下:

The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

它的主要作用是:expectedModCount保存的是遍历开始前的modCount,一旦开始遍历expectedModCount值不会发生改变,但是如果在遍历的时候又别的线程调用集合的add()或者remove方法就会引起modCount++,从而触发快速失败机制。

而对于快速失败机制而言,它的作用是: 当有线程遍历集合时,如果有线程想对集合进行添加或者删除元素的操作时,遍历集合的线程立马抛出异常,从而遍历集合的线程结束。即不允许遍历方法和添加删除方法同时执行。

迭代器的好处

  • 1、迭代器可以提供统一的迭代方式。
  • 2、迭代器也可以在对客户端透明的情况下,提供各种不同的迭代方式。
  • 3、迭代器提供一种快速失败机制,防止多线程下迭代的不安全操作。

不过对于第三点尚需注意的是:就像上述事例代码一样,我们不能保证迭代过程中出现“快速失败”的都是因为同步造成的,因此为了保证迭代操作的正确性而去依赖此类异常是错误的!