分享一個有意思的錯誤


subList方法拆分集合問題

JAVA技術交流群:737698533

分享一個有意思的錯誤,先看代碼

 public static void main(String[] args) throws IllegalAccessException {

        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }

        List<Integer> aList = list.subList(0, 2);
        List<Integer> bList = list.subList(2, 4);

        ArrayList<Integer> cList = new ArrayList<>();
     	cList.add(1);
     
        aList.addAll(cList);

        for (Integer i : bList) {
            System.out.println(i);
        }
 }

邏輯很簡單,將一個有10個元素的集合拆分為兩個集合aLisbList,然后創建一個新的集合cList,添加一個數據,之后調用addAll方法,將cList添加到aList中,最后遍歷bList

看着代碼沒啥問題吧,運行:

這個是啥錯呢?網上搜了一下大部分都是說在循環中不能對list集合進行修改,但是上面的代碼中並沒有在循環中修改啊???很迷惑

要想搞明白這個問題先來看看for循環的本質是什么

寫一段for循環的代碼.使用idea插件jclasslib可以看到,在代碼中使用的for循環,而編譯器給你編譯為字節碼后其實是一個迭代器

那么直接寫成迭代器的形式方便下面的觀察,將上面的代碼最后一段for循環改為

    Iterator<Integer> iterator = bList.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }

從list的subList方法入手

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

能看到如果使用了subList進行拆分,那么給你返回的不是一個創建時使用的ArrayList了,而是返回了一個SubList,可以通過反射來獲取類名證明返回的是一個subList類

繼續查看SubList這個類

發現它是一個ArrayList的內部類,同ArrayList都繼承了AbstractList類

注意這個SubList的有參構造最后一行,在調用subList方法后,就將當前List的modCount值賦值給了SubList類

那么現在有幾個問題:啥時候報的錯,為什么報錯,在哪報的錯,我們直接debug看

當走完addAll()方法后idea已經開始提示會出現bug,當我們繼續走完bList.iterator()方法后,程序出錯,然后退出

也就是說在調用iterator()方法后,出現的錯誤,我們繼續debug進入查看





到最后發現ArrayList的modCount和SubList類中的modCount判斷不同,所以才拋出的錯誤

那modCount是干啥的?簡單來講就是記錄當前集合被更改的次數

上面的三個問題已經解決了

啥時候報的錯:當調用iterator()方法時

為什么報錯:ArrayList的modCount和SubList類中的modCount值不同

在哪報的錯:bList的iterator()方法里

那么現在又有新的問題:ArrayList的modCount值什么時候改的?為什么對aList進行addAll操作,循環bList會出錯?

debug看看addAll()方法

而修改的這個屬性是在AbstractList當中的

那這兩個subList又不是同一個對象,咋能共用ArrayList中的modCount呢?

也確實不是同一個對象,但是這個兩個對象都是使用同一個List創建出來的,而他倆都是內部類

在創建subList時都有傳入過一個parent參數,傳入的參數都是this

我們直接看看這兩個subList類中的parent屬性是否一樣即可

因為List重寫了toString方法,無法通過toString看到地址,所以通過hashCode也可以來(大致)判斷是否是同一個對象

那么上面兩個問題也解決了

ArrayList的modCount值什么時候改的: 當調用addAll方法時進行修改的

為什么對aList進行addAll操作,循環bList會出錯: 因為外部類是同一個,修改的modCount是同一個,都在AbstractList當中,當循環bList實際上就是使用迭代器,調用iterator時會判斷ArrayList的modCount和當前的modCout,因為aList調用addAll方法導致AbstractList當中的modCount值進行了改變,因為aList和bList是同一個List創建出來的,他們的外部類是一樣的,那么bList判斷時就會出錯

越是不符合邏輯的地方,越埋藏着更深刻的邏輯


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM