2016-05-11 5 views
1

マップ(HashMap、HashTable、ConcurrentHashMap)を反復処理すると、特定のエントリを削除しようとすると、値。しかし、それはすべてのエントリで発生しません。マップ上で反復処理中に、すでに削除されていても値が出力される

public class Test { 
    public static void main(String[] args) { 
     ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>(); 
     map.put("Pujan", "pujan"); 
     map.put("Swati", "swati"); 
     map.put("Manish", "manish"); 
     map.put("Jayant", "pujan"); 
     System.out.println(map); 

     for (String string : map.keySet()) { 
     System.out.println(string+","+map.get(string)); 
     map.remove("Manish"); 
     } 
     System.out.println(map); 
    } 
    } 

出力:

{Jayant=pujan, Swati=swati, Manish=manish, Pujan=pujan} 
Jayant,pujan 
Swati,swati 
Pujan,pujan 
{Jayant=pujan, Swati=swati, Pujan=pujan} 

??第Scenarion

//map.remove("Manish"); 
map.remove("Swati"); 

出力:

{Jayant=pujan, Swati=swati, Manish=manish, Pujan=pujan} 
Jayant,pujan 
**Swati,null** 
Manish,manish 
Pujan,pujan 
{Jayant=pujan, Manish=manish, Pujan=pujan} 
+0

'map.remove'が' for'ループの中にあるのはなぜですか? – piyushj

+0

あなたは 'Map.remove'を反復して使用することは決してありません。ほとんどの場合、並行変更例外が発生し、そうでなければ予期しない結果が発生します。 – RealSkeptic

+0

@RealSkeptic - 彼はConcurrentHashMapを使用しています! –

答えて

1

ConcurrentHashMap戻りKeySetViewkeySet方法weakly consistentiterator、つまり、iteratorが返されます。返されるのは、作成時のセットのスナップショットです。

ConcurrentHashMap#keySet()のJavaドキュメントからビューのイテレータとspliteratorsは

弱い一貫している。しかし、あなたがmap.getを呼び出すときに変更がmap.getがあなたのためにnullを返しますを意味し、表示されています存在しないキーを要求する

1

keySet()のJavaDocs(Java8を前提としています)を確認してください[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#keySet--][1]

このマップに含まれるキーのセットビューを返します。セットはマップによってサポートされているため、マップの変更はセットに反映され、その逆もあります。このセットは、Iterator.remove、Set.remove、removeAll、retainAll、およびclearの各操作を使用して、このマップから対応するマッピングを削除する要素の削除をサポートしています。 addまたはaddAll操作はサポートされていません。

ビューのイテレータとスプライテータは弱く一貫しています。リンク

それらが正確に一度構築時に存在した要素をトラバースすることを保証し、(ただし保証されていない)構築後のすべての変更を反映しています。

これはあなたが見ているものです。

forループを制御するイテレータは、ビューを作成した後に変更を反映することを保証しません。

ビューは常に4つの元のエントリで構成されます。さて、 "Manish"の例では、イテレータに変更が反映されているため、文字列はManishを値として受け取ることはなく、map.get("Manish")を実行することはありません。悲しいかな、スワティのイテレーターは変更を反映しておらず、データは常に一貫していますが、スワチを第2の要素として提供しています。map.get("Swati")には、JavaDocsの変更が反映されています。

私はConcurrentHashMap.MapEntryのコードを読んでいませんが、「スワティ」ではなく「マニッシュ」でないとこのようなことが起こります。

  • イテレータが作成されました。状態:次= "Jayan"、ノード= ["Swati"、 "Manish"、 "Pujan"]
  • iterator.next()。 "Jayan"を返します。状態:要素がからなくなっている場合は、この時点で、次の= "スワティ"、ノード= [ "マニッシュ"、 "Pujan"]

、iterator.nextを行うには、()current = "スワティ" を返す(evneうその後のmap.get()で表示されるマップ)

"Manish"(または "Pujan")を削除すると、反復子はnodesを更新できます。しかし、 "Swati"を削除した場合は、currentにすでにそのキーがロードされているので遅すぎます。

関連する問題