簡體   English   中英

為什么這段代碼不拋出 ConcurrentModificationException?

[英]Why doesn't this code throw a ConcurrentModificationException?

為什么這段代碼不拋出ConcurrentModificationException 它在迭代時修改Collection ,不使用Iterator.remove()方法,這是刪除.

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String string : strings)
    if ("B".equals(string))
        strings.remove("B");
System.out.println(strings);

如果我用LinkedList替換ArrayList ,我會得到相同的結果。 但是,如果我將列表更改為("A", "B", "C", "D)或只是("A", "B")我會按預期得到異常。發生了什么?我正在使用jdk1.8.0_25如果相關。

編輯

我找到了以下鏈接

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078

相關部分是

天真的解決方案是向 AbstractList 中的 hasNext 添加協同修改檢查,但這會使協同修改檢查的成本增加一倍。 事實證明,只在最后一次迭代中進行測試就足夠了,這幾乎不會增加成本。 換句話說,hasNext 的當前實現:

 public boolean hasNext() { return nextIndex() < size; }

被這個實現取代:

 public boolean hasNext() { if (cursor != size()) return true; checkForComodification(); return false; }

由於 Sun 內部監管機構拒絕了此更改,因此不會進行此更改。 正式裁決表明,這一變化“已經證明了對現有代碼產生重大兼容性影響的潛力”。 (“兼容性影響”是修復有可能用 ConcurrentModificationException 替換沉默的不當行為。)

作為一般規則, ConcurrentModificationException檢測到修改時拋出,而不是導致 如果您在修改后從未訪問過迭代器,則它不會拋出異常。 不幸的是,這個微小的細節使得ConcurrentModificationException在檢測數據結構的誤用方面相當不可靠,因為它們僅在損壞完成后才會拋出。

這種情況不會拋出ConcurrentModificationException因為在修改后不會在創建的迭代器上調用next()

For-each 循環實際上是迭代器,因此您的代碼實際上如下所示:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iter = strings.iterator();
while(iter.hasNext()){
    String string = iter.next();
    if ("B".equals(string))
        strings.remove("B");
}
System.out.println(strings);

考慮在您提供的列表中運行的代碼。 迭代看起來像:

  1. hasNext()返回 true,進入循環,-> iter 移動到索引 0,string = "A",未移除
  2. hasNext()返回 true,繼續循環 -> iter 移動到索引 1,string = "B",已刪除。 strings現在長度為 2。
  3. hasNext()返回 false(iter 當前位於最后一個索引處,沒有更多索引可走),退出循環。

因此,當調用next()檢測到已進行修改時會拋出ConcurrentModificationException ,因此這種情況可以勉強避免這種異常。

對於您的其他兩個結果,我們確實得到了例外。 對於"A", "B", "C", "D" ,在刪除 "B" 后,我們仍然在循環中,並且next()檢測到ConcurrentModificationException ,而對於"A", "B"我想它是某種 ArrayIndexOutOfBounds 被捕獲並作為ConcurrentModificationException重新拋出

ArrayList 的迭代器中的hasNext就是

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

remove調用之后,迭代器在索引 2 處,列表的大小為 2,因此它報告迭代完成。 沒有並發修改檢查。 使用 ("A", "B", "C", "D) 或 ("A", "B") 時,迭代器不在列表的新末尾,因此調用next並引發異常.

ConcurrentModificationException只是一個調試輔助工具。 你不能依賴他們。

@Tavian Barnes 是完全正確的。 如果所討論的並發修改未同步,則不能保證拋出此異常。 引用java.util.ConcurrentModification規范:

請注意,不能保證快速失敗行為,因為一般來說,在存在非同步並發修改的情況下不可能做出任何硬保證。 快速失敗操作會盡最大努力拋出 ConcurrentModificationException。 因此,編寫一個依賴此異常來確保其正確性的程序是錯誤的: ConcurrentModificationException 應該僅用於檢測錯誤。

ConcurrentModificationException 的 JavaDoc 鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM