2014-11-24 74 views
2

注意:不是這個問題的重複:Why am I not getting a java.util.ConcurrentModificationException in this example?。問題是,爲什麼例外不是被拋出。List.remove奇怪的行爲

如果我們用List<String>foreach並嘗試從它,然後它拋出java.util.ConcurrentModificationException但爲什麼下面的代碼不拋出同樣的異常,也不能處理的User第二對象中刪除任何元素?

public class Common { 

    public static void main(String[] args) { 
     User user1 = new User(); 
     user1.setFirstname("Vicky"); 
     user1.setLastname("Thakor"); 

     User user2 = new User(); 
     user2.setFirstname("Chirag"); 
     user2.setLastname("Thakor"); 

     List<User> listUser = new ArrayList<User>(); 
     listUser.add(user1); 
     listUser.add(user2); 

     int count = 0; 
     for (User user : listUser) { 
      System.out.println(count + ":" + user.getFirstname()+" "+ user.getLastname()); 
      count++; 
      listUser.remove(user); 
     } 
    } 
} 

輸出是:

0:玉萍Thakor

+0

ArrayList的迭代器應該是一個失敗的快速迭代器,也許你只是沒有看到異常。此外,將示例更改爲每個人都可以編譯和運行的代碼可能會更好(不確定「User」是什麼)。從ArrayList的文檔中可以看出:'這個類的迭代器和listIterator方法返回的迭代器是快速失敗的:如果迭代器創建後隨時通過結構修改列表,除了通過迭代器自己的remove或add方法之外,迭代器會拋出一個ConcurrentModificationException.' – NESPowerGlove 2014-12-04 21:45:03

回答

4

雖然這個問題不是一個精確的複製,鏈接的問題:Why am I not getting a java.util.ConcurrentModificationException in this example?包含了答案。

用於驗證是否扔在迭代的方法next()呼籲異常製成,但這種情況只有在hasNext()回報true檢查。在你的情況,當列表有兩個元素,並刪除在第一次迭代的第一個條件:

public boolean hasNext() { 
     return cursor != size(); // 1 != 1 after first iteration 
} 

是偶然false,因爲cursor1,這就是size()是,此刻。因此不調用next(),並且不會拋出異常。

添加第三個元素:

listUser.add(user2); 

和異常將被拋出。但是,你不應該依賴這種行爲,因爲作爲the documentation解釋說,它不能保證:

注意,快速失敗行爲不能得到保證,一般來說,不可能作出任何硬性保證存在未同步的併發修改。快速失敗操作盡最大努力拋出ConcurrentModificationException。因此,編寫一個依賴於此異常的程序是正確的:編寫ConcurrentModificationException應該只用於檢測錯誤。

2

由於NESPowerGlove在註釋部分規定的迭代符根據Java文檔返回快速失敗的迭代器,但它包含這個

注意迭代器的快速失敗行爲不能保證 原樣,一般來說,不可能作出任何硬質保證 存在非同步併發修改。迭代器在盡力而爲 的基礎上拋出ConcurrentModificationException時出現快速失敗 。因此,編寫一個依賴於此例外的 的程序是錯誤的,因爲它的正確性: 迭代器的故障快速行爲應僅用於檢測錯誤。

(Emphasis mine) 因此,不保證在修改情況下拋出異常。

0

你的循環可能會被改寫爲

Iterator<User> iterator = listUser.iterator(); 
    while (iterator.hasNext()) { 
     User user = iterator.next(); 
     System.out.println(count + ":" + user.getFirstname() + " " + user.getLastname()); 
     listUser.remove(user); 
    } 

,如果我們展開循環,它看起來像

Iterator<User> iterator = listUser.iterator(); 
    System.out.println(iterator.hasNext()); // prints true, loop executes 
    User user = iterator.next(); 
    System.out.println(count + ":" + user.getFirstname() + " " + user.getLastname()); 
    listUser.remove(user); 

    System.out.println(iterator.hasNext()); // prints false, loop stops 

直到第二iterator.hasNext()調用,集合不會被改動,所有的都按預期工作。現在的問題是爲什麼第二個iterator.hasNext()調用返回false而不是拋出ConcurrentModificationException?讓我們來看看ArrayList的來源。我將引用JDK 8來源。

ArrayList.java,ArrayList的迭代器在類Itr的第840行中聲明。而且它的hasNext()方法很簡單:

int cursor;  // index of next element to return 
    ... 

    public boolean hasNext() { 
     return cursor != size; 
    } 

光標的下一個元素的返回指數,尺寸屬於外ArrayList實例。

檢查在next()方法中實現的協同作用。

請注意,您不得在您自己的代碼中依賴此檢查。它旨在打擊錯誤,而不是爲您提供依賴的邏輯。並不保證能夠捕捉到所有的錯誤(正如你的問題所證明的那樣)。性能可能是爲什麼在hasNext()方法中未實現此檢查的原因。

0

試試這個:

int count = 0; 
for (User user : listUser) { 
    listUser.remove(user); 
    System.out.println(count + ":" + user.getFirstname()+" "+ user.getLastname()); 
    count++; 
} 
for (User user : listUser) { 
    listUser.remove(user); 
    System.out.println(count + ":" + user.getFirstname()+" "+ user.getLastname()); 
    count++; 
} 

我在不到10秒創造了User類。包含兩個字符串。但對於這個問題,User類是無關緊要的。假設Object並且問題依然存在。關鍵是在調試時運行這個功能,看看會發生什麼。

如果添加相同的for-each循環並嘗試遍歷相同的列表,您將看到ConcurrentModificationException。第二次,它會顯示:

0:Vicky Thakor 
1:Chirag Thakor 

然後拋出異常。許多人不記得ConcurrentModificationExceptionRuntimeException,因此,沒有記錄在remove()方法的API上。這裏的問題是你應該相信文檔。在迭代過程中記錄的安全刪除元素的方法是使用Iterator