How to remove key value pair from Map during Iteration - Example tutorial

Suppose you have a Map e.g. HashMap or Hashtable, which contains key value pairs e.g. books and their prices, and you want to remove all books whose prices are greater than 40 USD, How do you that in Java? Many Java programmer, will say that they will iterate over Map and check each entry and then use remove(Object key) or remove(Object key, Object value) methods from java.util.Map to remove any mapping where the value is greater than 40 USD. Though the approach is right, the answer is wrong. Yes, we'll iterate over Map to check each value but we'll not use the two remove() methods from java.util.Map interface because they will throw ConcurrentModficationException, when you call them to remove mapping during iteration. Instead, we'll use the Iterator.remove() method to remove any key value pair, where the value is greater than 40 USD.

The Iterator is a common interface which allows you to go through each element of any Collection class including Map. Though, since Map doesn't implement Collection interface, you just cannot directly get an iterator from Map, but you can always get a view of Map and then get the iterator from those set e.g. set of keys by calling keySet() method. It returns set because of java.util.Map doesn't allow duplicate keys. You can also get a collection of values by calling values() method because values can repeat in Map, and set of entries by calling the entrySet()method.

These Set and Collection are backed by the actual map, hence any modification you do on this view will reflect in the original map. Apart from navigation method e.g. hasNext() and next(), Iterator also contains a remove() method which is used to remove the current element from the Collection you are iterating. This method should be used to delete any entry or key value pair from the map during iteration.


Even though, java.util.Map interface provides a couple of overloaded version of remove() method e.g. remove(Object key) which can be used to remove a mapping by key and remove(Object key, Object value) to remove a key value pair, they cannot be used when you are iterating over map using Iterator or enhanced for loop (remember Java 1.5 for loop is internally implemented using Iterator itself).

If you use them to remove mapping your code will throw ConcurrentModfiicationException, even if you are running your code on single thread environment. Yes, the word concurrent has confused many Java programmer from years, who get scared of getting this exception in a multithreading environment, but here concurrent is used in conjunction with iteration + any other operation which modifies the structure of Collection.

In short, always use Iterator's remove() method to remove a key value pair from Map while iterating over it. Here are exact steps to remove a key value pair from java.util.Map

1) Get a Set of keys or Set of entries by calling keySet() or entrySet() method of java.util.Map
2) Get the Iterator from key set or entry set.
3) Iterate over key set or entry set.
4) Check each value, if it satisfies criterion call iterator.remove() method

Once you finish iteration, the mappings which satisfy removal criterion should have been removed. Now, let's see a complete Java program to remove entries from Map.



Java Program to remove key value pairs while traversing a Map

In this program, I have a map of Java books and their prices, taken from Amazon.com. Basically, we have 5 best Java books and their prices and our task is to remove all books whose price is higher than 39 dollars. In order to do that, I'll iterate over Map and call Iterator.remove() method after checking the price of the book.

I am using entrySet() for traversing Map because it gives you entry, which contains both key and value. If you need both, then this is faster than traversing Map using a set of keys, because you need to perform a lookup to get the value.

If the price of the book is higher than 39 USD then we remove the book by calling the iterator's remove() method. We get the price by calling the getValue() method.


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/*
 * Java Program to remove key value pair from Map while 
 * iteration. 
 */
public class Demo {

  public static void main(String[] args) throws Exception {

    // create a Map to demonstrate example
    Map<String, Double> priceMap = new HashMap<String, Double>();

    // add some mapping e.g. popular Java books and their prices
    priceMap.put("Effective Java", 41.79);
    priceMap.put("Head First Java", 29.02);
    priceMap.put("Java Concurrency In Practice", 30.67);
    priceMap.put("Java SE 8 for Really Impatient", 31.99);
    priceMap.put("Head First Design Pattern", 39.05);

    // let's remove all books which are greater than 39.00 USD from map
    // get a set of entries
    Set<Entry<String, Double>> setOfEntries = priceMap.entrySet();

    // get the iterator from entry set
    Iterator<Entry<String, Double>> iterator = setOfEntries.iterator();

    // iterate over map
    while (iterator.hasNext()) {
      Entry<String, Double> entry = iterator.next();
      Double value = entry.getValue();

      if (value.compareTo(Double.valueOf(39.00)) > 0) {
        System.out.println("removeing : " + entry);
        // priceMap.remove(entry.getKey()); // wrong - will throw
        // ConcurrentModficationException
        // priceMap.remove(entry.getKey(), entry.getValue()); // wrong - will
        // throw error
        iterator.remove(); // always use remove() method of iterator
      }

    }
  }

}
Output
Removing: Head First Design Pattern=39.05
Removing: Effective Java=41.79


From the output, you can see that both Effective Java and Head First Design Patterns are removed because their price is higher than 39 USD but Map still contains other Java books e.g. Head First Java, Java Concurrency in Practice, and Java SE 8 for Really Impatient.


Our code is also free from ConcurrentModificaitonException becuase we are using Iterator's remove() method. If you uncomment the line which uses Map.remove() method then the code will throw ConcurrentMdofiicationException, as shwon below:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
at java.util.HashMap$EntryIterator.next(HashMap.java:1471)
at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
at Demo.main(Demo.java:34)

Don't confuse why you are getting concurrent modification exception even if just one thread is modifying the collection. The concurrent here doesn't mean multi-threading but simultaneously performing two operations e.g. iteration and removal.


That's all about how to remove a key value pair from Map during traversal. You should always use Iterator's remove() method to remove any mapping from the map while iterating over it to avoid any error. Use of Map.remove() method is prohibited during traversal because it throws ConcurrentMdoficiationException.

Further Reading
Java Fundamentals: The Core Platform
Java Fundamentals: Collections
Java SE 8 for Really Impatient


Other Java Collection tutorials you may like
How to sort a Map by keys and values in Java? (tutorial)
How to sort an ArrayList in ascending and descending order in Java? (tutorial)
Difference between ArrayList and HashSet in Java? (answer)
The difference between TreeMap and TreeSet in Java? (answer)
The difference between HashMap and ConcurrentHashMap in Java? (answer)
The difference between HashMap and LinkedHashMap in Java? (answer)
The difference between Hashtable and HashMap in Java? (answer)
The difference between HashSet and TreeSet in Java? (answer)
The difference between ArrayList and LinkedList in Java? (answer)
The difference between Vector and ArrayList in Java? (answer)
Difference between EnumMap and HashMap in Java

Thanks for reading this article so far. If you like this article then please share with your friends and colleagues. If you have any question or feedback then please drop a comment.

No comments :

Post a Comment