迭代器

原创
2015/11/12 19:04
阅读数 145
public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext()) {
//      list.remove("C");
        System.out.println(iterator.next());
    }
}

以上代码运行都没有问题,就是取出List数据,然后打印。

但是要是把注释打开那就会报错,java.util.ConcurrentModificationException,这里是为什么呢?那就来讨论一下迭代器的原理吧。

通过list.iterator查找发现上面截图,里面创建了Itr对象,下面就看看这个对象的具体内容。

private class Itr implements Iterator<E> {
    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;
    public boolean hasNext() {
        return cursor != size();
    }
    public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }
    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        checkForComodification();
        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

Itr是AbstractList的一个内部类。

方法hasNext:比对cursor(下一个要访问的元素的索引)与当前长度。

方法next:根据cursor值获取元素信息。

方法remove:删除lastRet(上一个访问元素的索引)值获取元素信息。

方法checkForComodification:检验(expectedModCount与modCount是否相等),前面的错误就是这里产生的。

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
        if (elementData[index] == null) {
            fastRemove(index);
            return true;
        }
    } else {
        for (int index = 0; index < size; index++)
        if (o.equals(elementData[index])) {
            fastRemove(index);
            return true;
        }
    }
    return false;
}
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // Let gc do its work
}

ArrayList的方法remove只是修改了参数modCount。

也就是在使用迭代器遍历的时候,就不能使用List自身的方法(add/remove)去修改原来的List的结构,要不会报错,而使用迭代器自身的方法(remove)是不会报错的,这又是为什么呢?

上图中的红框中明显发现,是把下一个访问的元素下标及上一个访问元素下标给修改了。

总结

JAVA的迭代器更像是数据库中的一个镜像,当你使用原始List修改了原始数据结构时,它会报错,必须使用迭代器的方法remove才可以不会报错。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部