Saturday, February 12, 2022

How to delete a key value pair from a HashMap during Iteration in Java - Example tutorial

Suppose you have a Map or Dictionaries like HashMap or Hashtable, which contains key-value pairs like books and their prices, and you want to delete all books whose prices are greater than 40 USD, how do you that in Java? This is one of the most common scenarios while developing Java applications and many Java programmers, will say that they will iterate over Map and check each entry and then use the remove(Object key) or remove(Object key, Object value) methods from java.util.Map to delete 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 delete any key-value pair, where the value is greater than 40 USD.

The Iterator is a common interface that allows you to go through each element of any Collection class including Map.

Though, since Map doesn't implement a 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 like,  set of keys by calling keySet() method.

It returns a set because of java.util.Map doesn't allow duplicate keys.  If you are not familiar with basic Collection classes in Java, like List, Set, and Map, I suggest you first go through a comprehensive course in Java, like The Complete Java MasterClass on Udemy. It explains all those fundamentals quite well.





How to delete an entry from a HashMap during Iteration? Example

You can also get a collection of values by calling the 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 the navigation method like 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 versions of the remove() method like the 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 the map using Iterator or enhanced for loop (remember Java 1.5 for-each 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 a single thread environment.

Yes, the word concurrent has confused many Java programmers for 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 the 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 the key set or entry set.
3) Iterate over key set or entry set.
4) Check each value, if it satisfies the criterion call iterator.remove() method

Once you finish iteration, the mappings which satisfy the removal criterion should have been removed. Though, if you want to learn more about Iterator and in general the Collection framework, I suggest you go through Java Fundamentals: Collections by Richard Richard Warburton on Pluralsight. It's a specialized course on Collections and covers the topic in-depth.



 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 like 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 shown 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 exceptions even if just one thread is modifying the collection. The concurrent here doesn't mean multi-threading but simultaneously performing two operations like 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.



Other Java Collection tutorials you may like
  • How to sort a Map by keys and values in Java? (tutorial)
  • My favorite free courses to learn Java in-depth (courses)
  • How to sort an ArrayList in ascending and descending order in Java? (tutorial)
  • My favorite free course to learn Object-Oriented Programming (courses)
  • Difference between ArrayList and HashSet in Java? (answer)
  • 7 Best courses to learn design patterns in Java? (courses)
  • The difference between TreeMap and TreeSet in Java? (answer)
  • Top 10 Courses to learn Data Structure and Algorithms in Java? (courses)
  • The difference between HashMap and ConcurrentHashMap in Java? (answer)
  • My favorite courses to learn Software Architecture? (courses)
  • 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 it with your friends and colleagues. If you have any questions or feedback then please drop a comment.

P. S. - If you are new to the Java world and looking for a free online training course to learn Java then you can also check out this Java Tutorial for Complete Beginners course on Udemy. It's completely free and you just need a Udemy account to join this online course.


6 comments :

Unknown said...

I am using Java1.8.0_65
I dont see concurrentModificationException while using map's remove method.

javin paul said...

Hello Ana, sounds interesting, can you please post your full code here?

Unknown said...

void testFinal(final int x, A testa){
testa = new A();
HashMap hm = new HashMap();
hm.put("Akey", 1);
hm.put("Bkey",2);
hm.put("Ckey",3);
hm.put("Dkey",4);
hm.put("Ekey",5);

Set> setOfEntries = hm.entrySet();
Iterator> it = setOfEntries.iterator();
while(it.hasNext()){
Entry e = it.next();
if(e.getValue()==3)
hm.remove(e.getKey());
}
System.out.println(hm);
}

Ashish Agarwal said...

@Ana, I run the code which you shared but I got ConcurrentModificationException

rajeev said...

@Ana, with iterator you can remove elements, but can't add elements to the map. If you use for loop, you can neither remove nor add elements. If you want to both add and remove then ConcurrentHashMap should be used.

rajeev said...

Hi Javin Paul, I think you can add removeIf() method that's present from Java 8 to this article. It will be helpful for many like me.

Post a Comment