foreach迴圈中為什麼不要進行remove/add操作
先來看一段程式碼,摘自阿里巴巴的java開發手冊
1 List<String> a = new ArrayList<String>(); 2 a.add("1"); 3 a.add("2"); 4 for (String temp : a) { 5 if("1".equals(temp)){ 6 a.remove(temp); 7 } 8 }
此時執行程式碼,沒有問題,但是需要注意,迴圈此時只執行了一次。具體過程後面去分析。再來看一段會出問題的程式碼:
List<String> a = new ArrayList<String>(); a.add("1"); a.add("2"); for (String temp : a) { if("2".equals(temp)){ a.remove(temp); } }
輸出為:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at luyudepackage.waitTest.main(waitTest.java:57)
是不是很奇怪?接下來將class檔案,反編譯下,結果如下
1 List a = new ArrayList(); 2 a.add("1"); 3 a.add("2"); 4 Iterator i$ = a.iterator(); 5 do 6 { 7 if(!i$.hasNext()) 8 break; 9 String temp = (String)i$.next(); 10 if("1".equals(temp)) 11 a.remove(temp); 12 } while(true);
幾個需要注意的點:
1.foreach遍歷集合,實際上內部使用的是iterator。
2.程式碼先判斷是否hasNext,然後再去呼叫next,這兩個函式是引起問題的關鍵。
3.這裡的remove還是list的remove方法。
先去觀察下list.remove()方法中的核心方法fastRemove()方法。
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 }
注意第二行,modCount++,此處先不表,下文再說這個引數。
順路觀察下list.add()方法
1 public boolean add(E e) { 2 ensureCapacityInternal(size + 1); // Increments modCount!! 3 elementData[size++] = e; 4 return true; 5 }
注意第二行的註釋,說明這個方法也會使modCount++
再去觀察下,iterator()方法
1 public Iterator<E> iterator() { 2 return new Itr(); 3 }
1 private class Itr implements Iterator<E> { 2 int cursor; // index of next element to return 3 int lastRet = -1; // index of last element returned; -1 if no such 4 int expectedModCount = modCount; 5 6 public boolean hasNext() { 7 return cursor != size; 8 } 9 10 @SuppressWarnings("unchecked") 11 public E next() { 12 checkForComodification();//萬惡之源 13 int i = cursor; 14 if (i >= size) 15 throw new NoSuchElementException(); 16 Object[] elementData = ArrayList.this.elementData; 17 if (i >= elementData.length) 18 throw new ConcurrentModificationException(); 19 cursor = i + 1; 20 return (E) elementData[lastRet = i]; 21 } 22 23 public void remove() { 24 if (lastRet < 0) 25 throw new IllegalStateException(); 26 checkForComodification(); 27 28 try { 29 ArrayList.this.remove(lastRet); 30 cursor = lastRet; 31 lastRet = -1; 32 expectedModCount = modCount; 33 } catch (IndexOutOfBoundsException ex) { 34 throw new ConcurrentModificationException(); 35 } 36 } 37 38 final void checkForComodification() { 39 if (modCount != expectedModCount) 40 throw new ConcurrentModificationException(); 41 } 42 }
幾個需要注意的點:
1.在iterator初始化的時候(也就是for迴圈開始處),expectedModCount = modCount,猜測是和當時list內部的元素數量有關係(已證實)。
2.當cursor != size的時候,hasNext返回true
3.next()函式的第一行,checkForComodification()這個函式就是報錯的原因 這個函式就是萬惡之源
4.第39行,mod != expectedModCount 就會丟擲ConcurrentModificationException()
接下來分析文章開頭的第一個例子,為啥不會報錯?
第一個例子執行完第一次迴圈後,mod = 3 expectedModCount =2 cursor = 1 size = 1 所以程式在執行hasNext()的時候會返回false,所以程式不會報錯。
第二個例子執行完第二次迴圈後,mod = 3 expectdModCount = 2 cursor = 2 size = 1 此時cursor != size 程式認定還有元素,繼續執行迴圈,呼叫next方法但是此時mod != expectedModCount 所以此時會報錯。
道理我們都懂了,再看一個例子
1 public static void main(String[] args) throws Exception { 2 List<String> a = new ArrayList<String>(); 3 a.add("1"); 4 a.add("2"); 5 for (String temp : a) { 6 System.out.println(temp); 7 if("2".equals(temp)){ 8 a.add("3"); 9 a.remove("2"); 10 } 11 } 12 }
此時輸出為:
1
2
顯然,程式並沒有執行第三次迴圈,第二次迴圈結束,cursor再一次等於size,程式退出迴圈。
與remove類似,將文章開頭的程式碼中remove替換為add,我們會發現無論是第一個例子還是第二個例子,都會丟擲ConcurrentModificationException錯誤。
原因同上,程式碼略。
手冊上推薦的程式碼如下
1 Iterator<String> it = a.iterator(); while(it.hasNext()){ 2 String temp = it.next(); if(刪除元素的條件){ 3 it.remove(); 4 } 5 }
此時remove是iterator的remove,我們看一下它的原始碼:
1 public void remove() { 2 if (lastRet < 0) 3 throw new IllegalStateException(); 4 checkForComodification(); 5 6 try { 7 ArrayList.this.remove(lastRet); 8 cursor = lastRet; //index of last element returned;-1 if no such 9 lastRet = -1; 10 expectedModCount = modCount; 11 } catch (IndexOutOfBoundsException ex) { 12 throw new ConcurrentModificationException(); 13 } 14 }
注意第10行,第8行,所以此時程式不會有之前的問題。
但是手冊上推薦的方法,在多執行緒環境還是有可能出現問題,一個執行緒執行上面的程式碼,一個執行緒遍歷迭代器中的元素,同樣會丟擲CocurrentModificationException。
如果要併發操作,需要對iterator物件加鎖。
平時遍歷list,然後刪除某個元素的時候,如果僅僅刪除第一個且刪除之後呼叫break //代表著此時不會再去執行iterator.next方法 也就不會觸發萬惡之源
而如果要刪除所有的某元素,則會報錯,謹記!
Ps再來看一個佐證
public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); for(int i : list){ System.out.println(i); if(i == 2){ list.remove((Object)2); } } }
此時只會輸出
1
2
當把remove物件改為3時候,再次報錯。
相關推薦
foreach迴圈中為什麼不要進行remove/add操作
先來看一段程式碼,摘自阿里巴巴的java開發手冊1 List<String> a = new ArrayList<String>(); 2 a.add("1"); 3 a.add("2"); 4 for (String temp : a) { 5
不要在foreach迴圈裡進行元素的remove/add操作
阿里巴巴java開發手冊的建議 在看阿里巴巴java開發手冊時,有一條建議是這樣的。 【強制】不要在foreach迴圈裡進行元素的remove/add操作。remove元素請使用Iterator方式,如果併發操作,需要對Iterator物件加鎖。 foreach遍歷集合,其實是走的Itera
為什麼在foreach迴圈中進行元素remove/add操作,會拋ConcurrentModificationException 異常?
執行以下程式碼: @Test public void test() { List<String> list = new ArrayList<>(); list.add("A"); list.add("B");
不要在 foreach 循環裏進行元素的 remove / add 操作
foreach remove在foreach 循環裏面,不能用remove方法,因為當list裏面的最後一個元素被remove時候,會報錯。摘自《阿裏巴巴Java開發手冊》【強制】不要在 foreach 循環裏進行元素的 remove / add 操作。 remove 元素請使用 Iterator方式,如果並
為什麼阿里禁止在 foreach 迴圈裡進行元素的 remove/add 操作
在阿里巴巴Java開發手冊中,有這樣一條規定: 但是手冊中並沒有給出具體原因,本文就來深入分析一下該規定背後的思考。 1.
為什麽禁止在 foreach 循環裏進行元素的 remove/add 操作
詳細 控制 string 得到 each lec 就是 編譯 分享 首先看下邊一個例子,展示了正確的做法和錯誤的錯發: 這是為什麽呢,具體原因下面進行詳細說明: 1、foreach循環(Foreach loop)是計算機編程語言中的一種控制流程語句,通
為什麽阿裏禁止在 foreach 循環裏進行元素的 remove/add 操作
調用鏈 不想 增強 arr 9.png 為我 相等 手冊 正確姿勢 在阿裏巴巴Java開發手冊中,有這樣一條規定:但是手冊中並沒有給出具體原因,本文就來深入分析一下該規定背後的思考。1.foreach循環foreach循環(Foreach loop)是計算機編程語言中的一種
for循環裏面不要進行remove操作,for循環裏remove元素後,list的下標會減小,導致遍歷不完全
body 代碼 2.0 class equals tostring arraylist 執行 align p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; text-align: justify; font: 12.0px "Helvetica
在foreach迴圈中對list結構進行新增或刪除
在迴圈中對list進行新增或刪除操作,會丟擲currentModifierException,因為在迴圈過程中動態的加入或刪除list元素會導致list的元素數量改變,出現漏項或者無限迴圈等現象。 ·如果要在foreach迴圈中刪除list中的元素,要使用itrator迭代
Random在for以及foreach迴圈中產生相同隨機數問題
在Random生成隨機數的時候,他是根據當前毫秒值來進行隨機生成,如果短時間內連續生成隨機數,就會導致生成的隨機數相同。下面我們介紹如何解決在 短時間內生成隨機數的時候,如何避免隨機數不一樣的問題。 利用Lock鎖住random引數 Random rand = new Random(); pub
16.1 foreach 迴圈中捕獲變數的變化
在 foreach 迴圈內的匿名函式(通常為Lambda表示式)中捕獲迴圈 變數時要格外小心。程式碼清單16-1就展示了這樣一個簡單的示例,它看上去似乎會輸出 x 、 y 、 z 。 1 string[] values = new string[] { "x", "y",
jquery實現json資料填充到table表格中並且實現remove add 資料
window.onload=function(){ //var articles = getAllDbdata(); getAllDbdata(); $("#removebtn").bind("click", delOneRow); $("#addbtn").bind("click",
為什麼阿里巴巴Java開發手冊中強制要求不要在foreach迴圈裡進行元素的remove和add操作?
在閱讀《阿里巴巴Java開發手冊》時,發現有一條關於在 foreach 迴圈裡進行元素的 remove/add 操作的規約,具體內容如下: 錯誤演示 我們首先在 IDEA 中編寫一個在 foreach 迴圈裡進行 remove 操作的程式碼: import java.util.ArrayList; imp
Java基礎之你會在foreach遍歷集合時進行remove操作嗎?
當通過for迴圈遍歷集合時,一般禁止操作(add or remove)集合元素。雖然開發規範裡寫的非常清楚,但最近還是有人掉坑裡導致出了一個小BUG,那我們就一起看看這麼做到底會發生什麼? 小例子 程式碼示例 List<String>
C# 對Foreach 的保護,因此在迭代的時候沒辦法remove add 等修改操作
InvalidOperationException: out of sync System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,UnityEngine.GameObject].VerifyState
for迴圈中進行Promise非同步操作的問題總結
筆者在for迴圈中進行Promise非同步操作的時候,主要會遇到兩個問題: 一是如何讓所有的for迴圈中的Promise操作結束後執行某個操作 二是迴圈中如果後一個Promise的執行依賴與前一個Promise的執行結果(例如對於某個資料庫操作
對比倆個Excel中的數據,並且進行數據操作
color 通過 類庫 分享 並且 多條 ado.net .cn server 項目需要, 去對比倆個Excel中的數據是否一樣, 不一樣需要做替換, 有個同事在處理中(處理了一天 1000 多條吧,移交給我), 本人在想通過人工手動的方式,一個個做對比得花多長時間啊, 主
ZBrush中如何對模型進行減面操作
del 分享 nbsp ati 凍結 cache 沒有 技術分享 技術 Decimation Master是ZBrush 4R8自帶的一個插件。中文名叫減面大師。其功能非常強大,也非常的方便,可以幫助我們提高效率,減少電腦資源損耗。作為一名3D美術師是必須掌握的一個技術。
對 url 中含有的中文進行轉碼操作
www 勘誤 utili reason main letter ins 兩個 int 對 url 中含有的中文進行轉碼操作 一般情況下,將帶有中文的 url 拷貝到開發工具,開發工具都會有相應的轉碼(自動轉碼), 現在大部分的瀏覽器也可以對含有中文的 url 進行轉碼(自
使用js方法將table表格中指定列指定行中相同內容的單元格進行合並操作。
var get 是否 for true ntb doc ide 就是 一、簡介 使用js方法對html中的table表格進行單元格的行列合並操作。 網上執行此操作的實例方法有很多,但根據實際業務的區別,大多不適用。 所以在網上各位大神寫的方法的基礎上進行了部分修改以適合自己