普通循環:利用index實現
增強型循環:通過迭代器實現
示例代碼:
public class ArrayListTest { public static void main(String[] args) { normalFor(getList()); //普通循環 iterator(getList()); //增強循環-迭代器 forEach(getList()); //增強循環-foreach方式 } //普通循環 private static void normalFor(List<String> list) { for (int i = 0 ; i < list.size() ; i++){ if ("b".equalsIgnoreCase(list.get(i)) || "c".equalsIgnoreCase(list.get(i))){ list.remove(i); } } System.out.println("normalFor:"+JSONObject.toJSONString(list)); } //增強循環-迭代器 private static void iterator(List<String> list) { Iterator iterator = list.iterator(); while (iterator.hasNext()){ String str = (String) iterator.next(); if ("b".equalsIgnoreCase(str) || "c".equalsIgnoreCase(str)){ iterator.remove(); } } System.out.println("iterator:"+JSONObject.toJSONString(list)); } //增強循環-foreach方式 private static void forEach(List<String> list) { for (String str : list){ if ("b".equalsIgnoreCase(str) || "c".equalsIgnoreCase(str)){ list.remove(str); } } System.out.println("forEach:"+JSONObject.toJSONString(list)); } private static List<String> getList(){ List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); return list; } }
輸出
normalFor:["a","c","d","e"] iterator:["a","d","e"] Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.qxy.collection.ArrayListTest.forEach(ArrayListTest.java:47) at com.qxy.collection.ArrayListTest.main(ArrayListTest.java:21) Process finished with exit code 1
從上邊可以看出
類型 | 輸出結果 |
普通循環 | 正常輸出,結果錯誤 |
增強循環-迭代器 | 正常輸出,結果正確 |
增強循環-foreach | 報異常 |
普通循環
普通循環,底層是數組,在remove操作時,被刪除元素的后邊所有的元素,會往前挪挪一位。咱們還是看圖,比較直觀
當第一次刪除時,此時的 i = 1,b正常刪除,c、d、e此時都往前挪了一位,然后執行了 i+1 變成了2,也就是d 的位置,一直往后都沒匹配到c,所以導致c為正常刪除。
增強循環-迭代器
在分析之前,我們先來看看反編譯之后的代代碼
public class ArrayListTest { ... private static void iterator(List<String> list) { Iterator iterator = list.iterator(); while(true) { String str; do { if (!iterator.hasNext()) { System.out.println("iterator:" + JSONObject.toJSONString(list)); return; } str = (String)iterator.next(); } while(!"b".equalsIgnoreCase(str) && !"c".equalsIgnoreCase(str)); iterator.remove();//不同的地方:調用迭代器的remove方法 } } private static void forEach(List<String> list) { Iterator var1 = list.iterator(); while(true) { String str; do { if (!var1.hasNext()) { System.out.println("forEach:" + JSONObject.toJSONString(list)); return; } str = (String)var1.next(); } while(!"b".equalsIgnoreCase(str) && !"c".equalsIgnoreCase(str)); list.remove(str);//不同的地方:調用list的remove方法 } } ... }
從上邊的代碼來看,迭代器 和 foreach 的方法很類似,唯一的區別就是 remove() 方法
迭代器調用的是 Iterator 類的 remove 方法
foreach調用的是 ArrayList類 的remove方法
那么我們去看下 他們各自 remove方法到底是怎么實現的
迭代器方式,那么需要先看 ArrayList.class
public Iterator<E> iterator() { return new Itr(); } /** * An optimized version of AbstractList.Itr */ 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; //這個屬性比較重要 Itr() {} ... public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); //第一步 try { ArrayList.this.remove(lastRet); //第二步:調用list的remove方法 cursor = lastRet; lastRet = -1; expectedModCount = modCount; //第三步:modCount是remove方法去維護更新,由於第一步中校驗 modCount 和 expectedModCount 是否相當等 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } ... final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
可以看到,list.iterator() 返回的是一個 Itr對象(ArrayList私有的實例內部類),執行 iterator.remove() 方法時,
第一步:先調用 checkForComodification() 方法,此方法作用:modCount 和 expectedModCount 是否相當
第二步:也就是foreach方式中調用的remove方法,在ArrayList內部的remove方法,會更新modCount屬性
第三步:將更新后的modCount重新賦值給expectedModCount變量,看這里!!!看這里!!!相比於有一個更新操作,才通過了上邊第一步的校驗!!!
到此,你可能還想看看,ArrayList類中的remove方法
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
看到此方法中,有一個modCount++的操作,也就是說,modCount會一直更新變化。
總結:jdk源碼離我們那么近,但是,總是這樣完美的擦肩而過,以后要多啃啃,不要錯過!!!