這個爲何要單獨說的緣由是,在開發中的對數據庫中的增刪爲最基本的,可是是否是寫對了就尤其重要java
先來看代碼:數據庫
1 public void testLoopInList(){ 2 List<String> a = new ArrayList<String>(); 3 a.add("1"); 4 a.add("2"); 5 a.add("w"); 6 for (String string : a) { 7 System.out.println(string); 8 } 9 10 for (String temp : a) { 11 if("2".equals(temp)){ 12 a.remove(temp); 13 } 14 } 15 16 for (String string : a) { 17 System.out.println(string); 18 }
輸出:oop
1 2 java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at test.Test_ForeachAndIterator.testLoopInList(Test_ForeachAndIterator.java:19)
出現錯誤了this
緣由:首先從錯誤中能夠看出,是ArraList中的Iterator的checkForComodification()出現的錯誤,說明了foreach的實現原理其實就是實現了內部類I特Iterator來進行遍歷的,其次爲何會出錯呢?spa
由於在ArrayList和ArrayList的內部類的Iterator中的都會存在remove的方法,而ArrayList和Iterator中都各自有本身的維持長度的變量,前者是modCount,後者是expectModCount,可是源碼中ArrayList的remove方法是會改變modCount的值,但卻不會直接同步到expectModCount的值的,而Iterator中時刻都插入了checkForComodification()方法來監測modCount是否與expectModCount相等,因此就會很容易出現異常錯誤,固然下面的代碼也是錯的code
1 public void testLoopInList(){ 2 List<String> a = new ArrayList<String>(); 3 a.add("1"); 4 a.add("2"); 5 6 for (String string : a) { 7 System.out.println(string); 8 } 9 10 Iterator<String> it = a.iterator(); 11 while(it.hasNext()){ 12 if("1".equals(it.next())){ 13 a.remove(it.next()); 14 } 15 } 16 17 for (String string : a) { 18 System.out.println(string); 19 } 20 }
錯誤緣由同上blog
因此在解決問題的關鍵就是要避免這個異常的出現,也就是時刻讓modCount==expectModCount,因此就是使用iterator的remove方法,由於會有使二者相等的代碼索引
即下面:ip
1 public void testLoopInList(){ 2 List<String> a = new ArrayList<String>(); 3 a.add("1"); 4 a.add("2"); 5 6 for (String string : a) { 7 System.out.println(string); 8 } 9 10 Iterator<String> it = a.iterator(); 11 while(it.hasNext()){ 12 if("1".equals(it.next())){ 13 it.remove(); 14 } 15 } 16 17 for (String string : a) { 18 System.out.println(string); 19 } 20 }
相關源碼提上:element
1 private class Itr implements Iterator<E> { 2 /** 3 * Index of element to be returned by subsequent call to next. 4 */ 5 int cursor = 0; 6 /** 7 * Index of element returned by most recent call to next or 8 * previous. Reset to -1 if this element is deleted by a call 9 * to remove. 10 */ 11 int lastRet = -1; 12 /** 13 * The modCount value that the iterator believes that the backing 14 * List should have. If this expectation is violated, the iterator 15 * has detected concurrent modification. 16 */ 17 int expectedModCount = modCount; 18 public boolean hasNext() { 19 return cursor != size(); 20 } 21 public E next() { 22 checkForComodification(); 23 try { 24 E next = get(cursor); 25 lastRet = cursor++; 26 return next; 27 } catch (IndexOutOfBoundsException e) { 28 checkForComodification(); 29 throw new NoSuchElementException(); 30 } 31 } 32 public void remove() { 33 if (lastRet == -1) 34 throw new IllegalStateException(); 35 checkForComodification(); 36 try { 37 AbstractList.this.remove(lastRet); 38 if (lastRet < cursor) 39 cursor--; 40 lastRet = -1; 41 expectedModCount = modCount; 42 } catch (IndexOutOfBoundsException e) { 43 throw new ConcurrentModificationException(); 44 } 45 } 46 final void checkForComodification() { 47 if (modCount != expectedModCount) 48 throw new ConcurrentModificationException(); 49 }
ArrayList中的remove
1 public boolean remove(Object o) { 2 if (o == null) { 3 for (int index = 0; index < size; index++) 4 if (elementData[index] == null) { 5 fastRemove(index); 6 return true; 7 } 8 } else { 9 for (int index = 0; index < size; index++) 10 if (o.equals(elementData[index])) { 11 fastRemove(index); 12 return true; 13 } 14 } 15 return false; 16 }
固然你說我不用for加強,就用普通的for ,就不會有建立Iterator的操做,進而就不會有Iterator的實時監測維護值得操做,也行啊,但仍是要注意有坑啊! 好比咱們來看下面的代碼:
1 List list = new ArrayList(); 2 Collections.addAll(list, 1, 2, 3, 4, 5); 3 for(int i = 0; i < list.size(); ++i){ 4 int val = (int) list.get(i); 5 6 // 需求是刪除 3 和 4 兩個元素 7 if(3 == val || 4 == val){ 8 list.remove(i); 9 } 10 }
這是咱們很是常見的寫法,結果 爲 :1 2 3 4
奇怪了,4爲甚沒有刪除掉,其實仔細想一想 仍是能相同的 刪除了3以後 i = 3 size = 4,這時候,list.get返回值爲5, 會跳過4
因此 修改的話,兩個 liter tips : 要麼本身維護索引,要麼反向循環
1 for(int i = list.size() - 1; i > 0; --i){ 2 int val = (int) list.get(i); 3 4 // 需求是刪除 3 和 4 兩個元素 5 if(3 == val || 4 == val){ 6 list.remove(i); 7 } 8 }
1 for(int i = 0; i < list.size(); ++i){ 2 int val = (int) list.get(i); 3 4 // 需求是刪除 3 和 4 兩個元素 5 if(3 == val || 4 == val){ 6 list.remove(i); 7 i --; // 刪除後,索引也應該變化 8 } 9 }
ok, that's all ...