使用迭代器Iterator遍歷集合元素時,如果刪除的元素不是倒數第二個數據,則會拋出ConcurrentModificationException異常
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("a");list.add("b");list.add("c"); for (Iterator<String> it = list.iterator();it.hasNext();){ String first = it.next(); if ("a".equals(first)) { list.remove(first); } System.out.println(first); } }
以上代碼運行結果如下圖:
原因:由於迭代器只負責對各種集合所包含的元素進行迭代,它自己並沒有保留集合元素。它判斷是否還有下一個元素的標准很簡單:如果下一步即將訪問的元素的索引不等於集合的大小,就會返回true,否則,返回false。當程序使用迭代器遍歷集合的倒數第2個元素時,下一步即將訪問的元素的索引為size()-1。如果此時通過List刪除集合的任意一個元素,將導致集合size()變成size()-1,這將導致hasNext()方法返回false,也就是說遍歷會提前結束,永遠訪問不到最后一個元素。迭代器在獲取下一個元素的next()方法中,會調用checkForComodification()方法來檢查集合是否被修改:遍歷之前使用expectedModCount保留該集合被修改的次數,每次獲取集合的下一個元素之前,檢查集合的當前修改次數modCount與遍歷之前的修改次數expectedModCount是否相等,如果不相等就直接拋出ConcurrentModification異常。源碼如下
hasNext()源碼:
public boolean hasNext() { return cursor != size; }
next()方法源碼:
public E next() { // 檢查是否已修改 checkForComodification(); 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(); }
如果使用普通的for循環刪除集合中元素,也會出現數據丟失問題:
List<String> list = new ArrayList<>(); list.add("a");list.add("b");list.add("c"); for (int i = 0;i<list.size();i++) { String first = list.get(i); System.out.println(first); if ("a".equals(first)) { list.remove(first); } } // 輸出結果為 a c
上述代碼為什么丟失了元素b呢?原因是:刪除元素a之后,集合中剩余的元素會“整體搬家”,所在的角標都會往前移動一位,此時元素b的角標由1變成了0,元素c的角標由2變成了1,而變量i此時的值為1,因此訪問時丟失了元素b
使用增強版的for循環會出現和Iterator遍歷時相同的問題
List<String> list = new ArrayList<>(); list.add("a");list.add("b");list.add("c"); for (String value:list) { System.out.println(value); if ("a".equals(value)) { list.remove(value); } }
如果想刪除集合中的某些元素,可以先定義一個變量,在遍歷集合時,將需要刪除的元素都添加到該變量中,在循環外統一刪除。
List<String> list = new ArrayList<>(); list.add("a");list.add("b");list.add("c"); List<String> deleteList = new ArrayList<>(); for (String value:list) { if ("a".equals(value)) { deleteList.add(value); } } if (deleteList.size() > 0) { list.removeAll(deleteList); deleteList = null; } // 輸出結果為 b c for (String value:list) { System.out.println(value); }