迭代器

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

可是要是把註釋打開那就會報錯,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的一個內部類。this

方法hasNext:比對cursor(下一個要訪問的元素的索引)與當前長度。spa

方法next:根據cursor值獲取元素信息。code

方法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。element

也就是在使用迭代器遍歷的時候,就不能使用List自身的方法(add/remove)去修改原來的List的結構,要不會報錯,而使用迭代器自身的方法(remove)是不會報錯的,這又是爲何呢?
rem

上圖中的紅框中明顯發現,是把下一個訪問的元素下標及上一個訪問元素下標給修改了。

總結

JAVA的迭代器更像是數據庫中的一個鏡像,當你使用原始List修改了原始數據結構時,它會報錯,必須使用迭代器的方法remove才能夠不會報錯。

相關文章
相關標籤/搜索