天天看點

list删除操作 java.util.ConcurrentModificationException

首先大家先看一段代碼:

public static void main(String[] args) {

      List<String> listStr = new ArrayList<String>();

      listStr.add("1");

      listStr.add("2");

      listStr.add("3");

      listStr.add("4");

      listStr.add("5");

     System.out.println("listStr.size:::"+listStr.size());  

      for(String str:listStr){

         if("3".equals(str)){

             listStr.remove(str);  

         }

      }

      System.out.println("listStr.size:::"+listStr.size());

現象:

      該程式會抛出一個  java.util.ConcurrentModificationException異常。

分析:

      對 Collection 或 Map 進行疊代操作過程中嘗試直接修改 Collection / Map 的内容時,會抛出java.util.ConcurrentModificationException 異常。 因為在修改資料時不能同步原有list中資料,當下一次循環時找不到資料。

解決辦法:

       Iterator 在工作的時候是不允許被疊代的對象被改變的,使用 Iterator 本身的方法 remove() 來删除對 

象, Iterator.remove() 方法會在删除目前疊代對象的同時維護索引的一緻性。

改後代碼:

public static void main(String[] args) {

      List<String> listStr = new ArrayList<String>();

      listStr.add("1");

      listStr.add("2");

      listStr.add("3");

      listStr.add("4");

      listStr.add("5");

      System.out.println("listStr.size:::"+listStr.size());

      Iterator<String> ite = listStr.iterator();

      while(ite.hasNext()){

          String str = (String)ite.next();

          if("3".equals(str)){

             ite.remove();

          }

      }

      System.out.println("listStr.size:::"+listStr.size());

}

補充:

      或許有人可能會問如果我不用增強for循環,直接用.get(index)方法會不會報錯?

   代碼如下:

      for(int i = 0 ; i < listStr.size(); i++){

           if("3".equals(listStr.get(i))){

               listStr.remove(i);

           }

      }

  現象:

        listStr.size:::5

        listStr.size:::4

        大家可以看到通過list的下表索引來修改list資料是不會出錯的。

  分析:

       大家可以這樣認為:通過所引來循環時,jvm記錄的是所引值,當移除目前對象後,其他元素的索引号不會同步改變。下次循環仍可以找到對應資料。而增強for循環,記錄的是目前對象,當下次循環時,會先找到該對象,然後遊标向下移,這時候找不到對象是以結果會混亂。

 java.util.ConcurrentModificationException

【引言】

經常在疊代集合元素時,會想對集合做修改(add/remove)操作,類似下面這段代碼:

[java]  view plain copy  

  1. for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {  
  2.     Integer val = it.next();  
  3.     if (val == 5) {  
  4.         list.remove(val);  
  5.     }  
  6. }  

運作這段代碼,會抛出異常java.util.ConcurrentModificationException。

【解惑】

(以ArrayList來講解)在ArrayList中,它的修改操作(add/remove)都會對modCount這個字段+1,modCount可以看作一個版本号,每次集合中的元素被修改後,都會+1(即使溢出)。接下來再看看AbsrtactList中iteraor方法

[java]  view plain copy  

  1. public Iterator<E> iterator() {  
  2.     return new Itr();  
  3. }  

它傳回一個内部類,這個類實作了iterator接口,代碼如下:

[java]  view plain copy  

  1. private class Itr implements Iterator<E> {  
  2.     int cursor = 0;  
  3.     int lastRet = -1;  
  4.     int expectedModCount = modCount;  
  5.     public boolean hasNext() {  
  6.         return cursor != size();  
  7.     }  
  8.     public E next() {  
  9.         checkForComodification();  
  10.         try {  
  11.             E next = get(cursor);  
  12.             lastRet = cursor++;  
  13.             return next;  
  14.         } catch (IndexOutOfBoundsException e) {  
  15.             checkForComodification();  
  16.             throw new NoSuchElementException();  
  17.         }  
  18.     }  
  19.     public void remove() {  
  20.         if (lastRet == -1)  
  21.             throw new IllegalStateException();  
  22.         checkForComodification();  
  23.         try {  
  24.             AbstractList.this.remove(lastRet);  
  25.             if (lastRet < cursor)  
  26.                 cursor--;  
  27.             lastRet = -1;  
  28.             // 修改expectedModCount 的值  
  29.             expectedModCount = modCount;  
  30.             } catch (IndexOutOfBoundsException e) {  
  31.             throw new ConcurrentModificationException();  
  32.         }  
  33.     }  
  34.     final void checkForComodification() {  
  35.         if (modCount != expectedModCount)  
  36.             throw new ConcurrentModificationException();  
  37.     }  
  38.     }  

在内部類Itr中,有一個字段expectedModCount ,初始化時等于modCount,即當我們調用list.iterator()傳回疊代器時,該字段被初始化為等于modCount。在類Itr中next/remove方法都有調用checkForComodification()方法,在該方法中檢測modCount == expectedModCount,如果不相當則抛出異常ConcurrentModificationException。

前面說過,在集合的修改操作(add/remove)中,都對modCount進行了+1。

在看看剛開始提出的那段代碼,在疊代過程中,執行list.remove(val),使得modCount+1,當下一次循環時,執行 it.next(),checkForComodification方法發現modCount != expectedModCount,則抛出異常。

【解決辦法】

如果想要在疊代的過程中,執行删除元素操作怎麼辦?

再來看看内部類Itr的remove()方法,在删除元素後,有這麼一句expectedModCount = modCount,同步修改expectedModCount 的值。是以,如果需要在使用疊代器疊代時,删除元素,可以使用疊代器提供的remove方法。對于add操作,則在整個疊代器疊代過程中是不允許的。 其他集合(Map/Set)使用疊代器疊代也是一樣。

//獲獎顯示
    public String prizeList(){
        voteList = userWorksService.getAllRank();
        UserWorks userWorks = new UserWorks();
        Iterator<UserWorks> list = voteList.iterator();
        Integer rank ;
        while(list.hasNext()){
            userWorks = list.next();
            if(!("9".equals(userWorks.getAwardsRank()))){
                rank = Integer.parseInt(userWorks.getAwardsRank())+2;
                userWorks.setAwardsRank(rank +"");
                allList.add(userWorks);
            }
        }
        voteList.removeAll(allList);
        return "prizeList";
    }