Exception in thread "main" java.util.ConcurrentModificationException 併發修改異常引起的思考!java
1 foreach循環刪除元素安全
①list遍歷刪除元素時會報錯,好比下面刪除字符串"aa",也有遍歷不報錯的例子,看下面的例子併發
public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for (String str : array) {
if("aa".equals(str)){
array.remove(str);
}
}
System.out.println(array.size());
}
}
console: java.util.ConcurrentModificationException
②下面刪除字符串"aa"不會報錯spa
public class TestMain { public static void main(String[] args) { ArrayList<String> array = new ArrayList<String>(); array.add("cc"); array.add("aa"); array.add("bb"); for (String str : array) { if("aa".equals(str)){ array.remove(str); } } System.out.println(array.size()); } } console : 2
提出問題:爲何上面都是遍歷刪除,第二個確沒有報錯呢?code
結論:其實緣由很簡單,由於第二個例子沒有走iterator的next方法,刪除了字符串"aa"以後,執行hasNext方法返回false直接退出遍歷了,hasNext中就是判斷cursor != size;此時的cursor是2,而size正好也是2,因此退出了遍歷。blog
而第一個例子刪除字符串"aa"以後,cursor=2,size=3,因此hasNext方法返回的true,會執行next方法。索引
注:只要遍歷中remove了,expectedModCount和modCount就不相等了。element
注2:cursor 就相似遊標,這裏表示第幾回,size就是元素個數rem
同理:iterator的形式遍歷同foreach。字符串
2 上面都是經過foreach的方式進行的,若是用普通for循環會怎麼樣
public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for(int i = 0;i < array.size();i++){
if("aa".equals(array.get(i))){
array.remove(i);
}
}
System.out.println(array.size());
}
}
console: 2
結論: 普通for循環能夠正常刪除,他是根據索引進行刪除的,因此沒有影響。
根據報錯信息能夠看到是進入checkForComodification()方法的時候報錯了,也就是說modCount != expectedModCount。具體的緣由,是在於foreach方式遍歷元素的時候,是生成iterator,而後使用iterator遍歷。在生成iterator的時候,
會保存一個expectedModCount參數,這個是生成iterator的時候List中修改元素的次數。若是你在遍歷過程當中刪除元素,List中modCount就會變化,若是這個modCount和exceptedModCount不一致,就會拋出異常,這個是爲了安全的考慮。
看看list的remove源碼:
1 private void fastRemove(int index) { 2 modCount++; 3 int numMoved = size - index - 1; 4 if (numMoved > 0) 5 System.arraycopy(elementData, index+1, elementData, index, 6 numMoved); 7 elementData[--size] = null; // clear to let GC do its work 8 }
remove操做致使modCount++
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(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
對標題的答案: iterator遍歷不能remove操做,會報併發修改異常,只有當你remove的元素時倒數第二個元素時不會拋出異常。
以上屬於我的心得,不對之處還望大佬指出,若是對你有幫助記得點贊。