foreach
阿里巴巴java開發手冊
【強制】不要在foreach循環里進行元素的remove/add操作。remove元素請使用Iterator方式,如果並發操作,需要對Iterator對象加鎖。
反例
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if("1".equals(temp)){
a.remove(temp);
}
}
正例
Iterator<String> it= a.iterator();
while(it.hasNext()){
String temp = it.next();
if(刪除元素的條件){
it.remove();
}
}
foreach源碼
foreach遍歷集合,其實是走的Iterator,首先判斷hasNext(),如果沒有了則終止循環,否則next()獲取元素時,next()時,都要check一下集合元素個數是否變化了,如果變化了,則拋出異常。
Itr是ArrayList的內部類,實現了Iterator接口
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;
public boolean hasNext() {
return cursor != size;//游標不等於元素個數就是還有下一個
}
public E next() {
checkForComodification();//check是否並發修改
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是預期的修改次數。增刪操作會使得modCount+1,不等於expetedModCount,所以拋出異常。
沒有使用list.iterator時調用的是ArrayList自己的remove,並不會同步這兩個值,導致拋出異常。調用了ArrayList.iterator之后,然后了Itr對象,此后再remove,remove方法中有讓這兩個值相等的操作。
迭代器方式移除
那為什么Iterator不會異常呢?
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;//這里預期的修改次數改為實際修改次數
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
迭代器的remove方法會修改expectedModCount,從而使modCount與之相等
參考:https://blog.csdn.net/wangjun5159/article/details/61415358