解析源碼通過代碼演示爲什麼迭代器,進行倒數第二個數的修改時不會報ConcurrentModificationException異常

Iterable 接口是collection的父接口,他提供了Iterator方法來創建迭代器,意味着collection集合都有迭代能力

其他的注意事項不一一列舉,今天詳細解釋一下,在集合遍歷的過程中,不能給集合進行增刪操作,會報ConcurrentModificationException異常,以及爲什麼刪除倒數第二個元素的時候不會報錯的問題.

public class Demo01 {
    public static void main(String[] args) {
        Collection<String> test = new ArrayList<>();
        test.add("AAAA");
        test.add("BBBB");
        test.add("CCCC");
        test.add("DDDD");

        Iterator<String> iterator = test.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            System.out.println(next);
            if("BBBB".equals(next)){
                test.remove("BBBB");
            }
        }
    }
}

在以上示例中

集合test有4個元素,我們在迭代的過程,remove第二個元素(注意這裏首先試一下刪除非倒數第二個元素的情況);

執行結果:

"C:\Program Files\Java\jdk-11.0.5\bin\java.exe" "-javaagent:C:\Program Files (x86)\IDEA\JetBrains\IntelliJ IDEA\lib\idea_rt.jar=5517:C:\Program Files (x86)\IDEA\JetBrains\IntelliJ IDEA\bin" -Dfile.encoding=UTF-8 -classpath D:\IDEAWorkSpace\day05\out\production\day05 com.cpic._01迭代器的練習.Demo01
AAAA
BBBB
Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
	at com.cpic._01迭代器的練習.Demo01.main(Demo01.java:26)

發現報錯了,查看源碼進行了解一下,

首先是hadNext()方法的源碼:

public boolean hasNext() {
            return cursor != size;
        }

 就一句話,比較cursor和size是否相等,

其次查看Next()方法的源碼:

        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];
        }

 首先是調用checkForComodification()方法,

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

 比較modCount和expectedModCount是否相等(這邊請留意一下),其餘剩餘的就是判斷和拋異常了.

最後看一下remove(Object o)方法的源碼:

    public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
        fastRemove(es, i);
        return true;
    }

 一開始是一些賦值判斷操作,也暫時不做說明,主要看一下最後調用的fastRemove(es,i)方法

    private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

 重點來了,首先是modCount++,其次是一個System.arraycopy方法.

這裏額外講解一下System.arraycopy方法,

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):將數組中指定的數據拷貝到另一個數組中。數組的拷貝動作是系統級的,性能很高。 五個參數分邊是原數組,原數組索引起始位置,目標數組,目標數組索引起始位置,複製元素的個數,

重要方法的代碼講解完後,進行代碼流程講解.

第一步: 創建test集合並賦值,這裏進行了4次add操作,所以modCount爲4(這個參數是記錄操作次數的)並執行hasNext()

 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;

所以expectedModCount的數值爲4

統計各個參數的數值

cursor = 0 (初始化的)

lastRet = -1 (初始化的)

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第二步: cursor != size,進入next方法 :在checkForComodification方法中,ModCount  == expectedModCount 所以繼續執行.

next中的兩個if都不滿足,所以參數賦值,return繼續進行,輸出結果AAAA

cursor = 1

lastRet = 0

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第三步: 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法

第四步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果BBBB

cursor = 2

lastRet = 1

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第五步: equals條件滿足,進入remove方法,結合源碼,前面是進行一系列賦值,判斷,循環,最後fastRemove方法參數,es爲集合,i = 1

首先modCount++;

然後System.arraycopy(es,2,es,1,2)

es{AAAA,BBBB,CCCC,DDDD},從索引爲2開始,截取2個元素,賦值到es中,索引從1開始,執行結果

es{AAAA,CCCC,DDDD},現在就是remove掉BBBB的結果

cursor = 2

lastRet = 1

size = 3

ModCount = 5

expectedModCount = 4

第六步: 執行hasNext方法,cursor != size,進入next方法

第七步: 在checkForComodification時, modCount != expectedModCount,所以報錯ConcurrentModificationException

===================================分割線===============================================

現在講解爲什麼remove倒數第二個數不會報錯.

代碼將BBBB改爲CCCC即可

public class Demo01 {
    public static void main(String[] args) {
        Collection<String> test = new ArrayList<>();
        test.add("AAAA");
        test.add("BBBB");
        test.add("CCCC");
        test.add("DDDD");

        Iterator<String> iterator = test.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            System.out.println(next);
            if("CCCC".equals(next)){
                test.remove("CCCC");
            }
        }
    }
}

第一步: 創建test集合並賦值,並執行hasNext(),統計各個參數的數值

cursor = 0 (初始化的)

lastRet = -1 (初始化的)

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第二步: cursor != size,進入next方法 :在checkForComodification方法中,ModCount  == expectedModCount 所以繼續執行.

next中的兩個if都不滿足,所以參數賦值,return繼續進行,輸出結果AAAA

cursor = 1

lastRet = 0

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第三步: 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法

第四步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果BBBB

cursor = 2

lastRet = 1

size = 4 (集合長度)

ModCount = 4

expectedModCount = 4

第五步, 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法

第六步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果CCCC

cursor = 3

lastRet = 2

size = 4

ModCount = 4

expectedModCount = 4

第七步: equals條件滿足,進入remove方法,結合源碼,前面是進行一系列賦值,判斷,循環,最後fastRemove方法參數,es爲集合,i = 2

首先modCount++;

然後System.arraycopy(es,3,es,2,1)

es{AAAA,BBBB,CCCC,DDDD},從索引爲3開始,截取1個元素,賦值到es中,索引從2開始,執行結果

es{AAAA,BBBB,DDDD},現在就是remove掉BBBB的結果

cursor = 3

lastRet = 2

size = 3

ModCount = 5

expectedModCount = 4

第八步: 執行hasNext方法,cursor == size,結束循環,沒有進入next中的checkForComodification給程序報錯的機會,所以沒有報錯

===============================================================================================

以上就是,對該問題的講解,由於書面表達流程,所以比較冗餘.如果有什麼疑問或者見解,歡迎大傢俬信.

推薦大家結合debug進行驗證,對結果更加鮮明.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章