List集合增強for回圈時產生的例外
1.介紹
在List 集合使用增強for回圈遍歷時,我們如果改變了集合的長度,會拋出例外,下面舉個例子:
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for (String s : list) {
if("a".equals(s)){
//list.remove(s);
list.add("a++");
}
}
}
例外資訊:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
···
2.增強for回圈的原理
增強for回圈是Java提供的語法糖,比如
for (Integer i : list) {
System.out.println(i);
}
反編譯后,其實是這樣的:
Integer i;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
i = (Integer)iterator.next();
}
3.現在我們通過源代碼來分析一開始例子中所報的錯誤:
位置是在ArrayList的850行和899行
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
拋出例外的條件是 modCount != expectedModCount ,
提到的幾個關鍵變數:
-
cursor:表示下一個要訪問的元素的索引,從next()方法的具體實作就可看出
-
lastRet:表示上一個訪問的元素的索引
-
expectedModCount:表示對ArrayList修改次數的期望值,它的初始值為modCount,
-
modCount是AbstractList類中的一個成員變數
modCount表示對List的修改次數,他的初始值是0,查看ArrayList的add()和remove()方法就可以發現,每次呼叫add()方法或者remove()方法就會對modCount進行加1操作,
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
看到這里,問題的原因找到了,在直接呼叫add()或者remove()方法時,只修改了modCount,卻沒有修改expectedModCount!
ArrayList中給出的解決辦法是提供了一個ListIterator:
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {}
它實作的迭代器實作了add(E e) 和remove()方法,可以解決拋出例外的問題,這兩個方法里都修改了expectedModCount,讓他和modCount保持一致,
注意:上述兩個方法在使用時要保證環境的執行緒安全,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/233101.html
標籤:其他
上一篇:除錯最長的一幀(第三天)
下一篇:分析網站登錄處的加密演算法(二)
