簡體   English   中英

為什么從 ConcurrentHashMap 中刪除第一個條目不會立即反映在迭代器中,但刪除第二個或后續條目是?

[英]Why is removing the 1st entry from a ConcurrentHashMap not immediately reflected in the iterator, but removing the 2nd or subsequent entries is?

我創建了一個 iterator(),然后在迭代之前從 map 中刪除了第一個條目。 我總是得到從迭代器返回的第一個項目。 但是當我刪除第二個或后續條目時,當前迭代器不會返回該條目。

從 map 中刪除第一個條目的示例:

    Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
    m1.put(4, 1);
    m1.put(5, 2);
    m1.put(6, 3);
    Iterator i1 = m1.entrySet().iterator();
    m1.remove(4);        // remove entry from map
    while (i1.hasNext())
        System.out.println("value :: "+i1.next()); //still shows entry 4=1

output 是:

value :: 4=1
value :: 5=2
value :: 6=3

從 map 中刪除第三個條目的示例:

    Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
    m1.put(4, 1);
    m1.put(5, 2);
    m1.put(6, 3);
    Iterator i1 = m1.entrySet().iterator();
    m1.remove(6);        // remove entry from map
    while (i1.hasNext())
        System.out.println("value :: "+i1.next()); //does not show entry 6=3

output 是:

value :: 4=1
value :: 5=2

為什么從 map 中刪除第一個條目未反映在迭代器中,但刪除第二個或后續條目是?

Java 文檔說:

迭代器、拆分器和枚舉在創建迭代器/枚舉時或之后的某個時間點返回反映 hash 表的 state 的元素。 它們不會拋出 ConcurrentModificationException。

這意味着,它的迭代器在創建迭代器時反映了 hash 表的 state。 當我們在 Map 中添加或刪除條目時,迭代器將顯示原始條目?

根據這個迭代器和分裂器是弱一致的。 “弱一致”的定義可以在這里找到:

大多數並發集合實現(包括大多數隊列)也不同於通常的 java.util 約定,因為它們的迭代器和拆分器提供弱一致而不是快速失敗遍歷:

  • 它們可能與其他操作同時進行
  • 他們永遠不會拋出 ConcurrentModificationException
  • 它們保證遍歷元素,因為它們在構造時就存在一次,並且可能(但不保證)反映構造后的任何修改。

這意味着在創建迭代器之后所做的任何修改都可能會被反映,但不能保證。 這只是並發迭代器\拆分器的正常行為。

為了實現一次迭代行為,當您通過迭代器 object 刪除元素時,需要更新迭代器數據結構以使其與集合發生的情況保持同步。 這在當前實現中是不可能的,因為它們不保留與未完成迭代器的鏈接。 如果他們這樣做了,他們將需要使用參考對象或冒 memory 泄漏的風險。

迭代器保證在創建時反映 map 的 state。 進一步的變化可能會反映在迭代器中,但它們不是必須的。

將 Iterator 視為 LinkedList,並且您可以隨身攜帶頭部參考。 如果您碰巧從鏈表中刪除了 head 並且沒有重置 head.next 值並從 head 開始迭代,您仍然會從同一個 head 遍歷,因為您使用的是過時的 head 引用。 但是,當您刪除非頭部元素時,先前的 element.next 會更新。

答案在您引用的文檔中:

迭代器、拆分器和枚舉在創建迭代器/枚舉時或之后的某個時間點返回反映 hash 表的 state 的元素。

之后。 自迭代器創建以來,迭代器可能會或可能不會顯示對 map 的更改。

設計人員在並發修改和迭代期間強制執行更精確的行為是不切實際的,但它並沒有被破壞。

實際值檢查在next()中完成,

public final Map.Entry<K,V> next() {
    Node<K,V> p;
    if ((p = next) == null)
        throw new NoSuchElementException();
    K k = p.key;
    V v = p.val;
    lastReturned = p;
    advance();
    return new MapEntry<K,V>(k, v, map);
}

如果可能, advance方法前進,返回下一個有效節點,如果沒有,則返回 null。

所以對於第一個條目K k = 4; V v = 1; K k = 4; V v = 1; 即使它被刪除。 但是對於隨后的k,v將決定從advance()更新

因此,如果您在remove之后調用next() ,它將不存在(這很明顯),

Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
m1.put(4, 1);
m1.put(5, 2);
m1.put(6, 3);
Iterator<Map.Entry<Integer, Integer>> i1 = m1.entrySet().iterator();
m1.remove(4);        // remove entry from map
i1.next();  

while (i1.hasNext())
    System.out.println("value :: "+i1.next());

暫無
暫無

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

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