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個元素的集合拆分為兩個集合aLis和bList,然后創建一個新的集合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判斷時就會出錯
越是不符合邏輯的地方,越埋藏着更深刻的邏輯