pujan jain
pujan jain

Reputation: 169

While iterating over a map, for a value, it gets printed though it is already removed

When I iterate over map (HashMap,HashTable,ConcurrentHashMap), when I try to remove particular entry, in iteration it still prints it with null value. But it does not happen with every entry.

  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);
    }
  }

output:

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

??Second Scenarion

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

Output:

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

Upvotes: 1

Views: 103

Answers (2)

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23329

The keySet method in ConcurrentHashMap returns a KeySetView which has a weakly consistent iterator, in other words, the iterator it returns is a snapshot of the set at the time it's created.

From the ConcurrentHashMap#keySet() Java docs

The view's iterators and spliterators are weakly consistent

But when you call map.get the changes are visible, which means the map.get will return null because you asking for a key that doesn't exist

Upvotes: 1

Galo Navarro
Galo Navarro

Reputation: 460

Check out the JavaDocs (assuming Java8) for keySet() [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#keySet--][1]

Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. The set supports element removal, which removes the corresponding mapping from this map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.

The view's iterators and spliterators are weakly consistent.

Which links to:

they are guaranteed to traverse elements as they existed upon construction exactly once, and may (but are not guaranteed to) reflect any modifications subsequent to construction.

This is what you're seeing.

The iterator which governs the for loop doesn't guarantee to reflect changes after you constructed the view.

The View is always constructed with the 4 original entries. Now, in the example with "Manish", it just happens that the iterator does reflect the modification, so string never takes Manish as value, and you never do map.get("Manish"). Alas, with Swati the iterator is not reflecting the modification and it still gives you "Swati" as a second element although the data is always consistent: a map.get("Swati") does reflect the change, as per the JavaDocs.

I haven't read the code in ConcurrentHashMap.MapEntry (you should), but the reason why this happens with "Swati" and not "Manish" is very likely the following.

  • Iterator created. State: next = "Jayan", nodes = ["Swati", "Manish", "Pujan"]
  • iterator.next(). Returns "Jayan". State: next = "Swati", nodes = ["Manish", "Pujan"]

At this point, doing iterator.next() would return current = "Swati" (evne if the element is gone from the map, which shows with the subsequent map.get())

If you delete "Manish" (or "Pujan"), the Iterator is able to update nodes. But if you delete "Swati" it's too late as current was already loaded with that key.

Upvotes: 1

Related Questions