遍歷集合時刪除集合中的元素問題


使用迭代器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);
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM