Because I am constantly busy working on something, I have never had time to actually put everything in words and pictures. But, since you got here, then you must have already seen some part of my work - and this is the way I’m talking.I'm 23, born in Romania, student at UPG Romania in software development field. I started from 0, mostly with basic stuff, and I’m evolving every day to an expert. I'm focused on freelancing projects, from small websites, to really heavy stuff. I know that I look and act differently from most developers, but this is why you will love to work with me! Constantin has posted 42 posts at DZone. You can read more from them at their website. View Full User Profile

Removing entries from a HashMap

09.27.2011
| 23959 views |
  • submit to reddit

There are a few solutions for looping a HashMap, but one of the most elegant looks like below:

HashMap<String, Integer> map = new HashMap<String, Integer>();
  map.put("One", 1);
  map.put("Two", 2);
  map.put("Three", 3);
  map.put("Four", 4);
  map.put("Five", 5);
  map.put("Six", 6);
  map.put("Seven", 7);
  map.put("Eight", 8);
  map.put("Nine", 9);
  map.put("Ten", 10);
        
  for (Map.Entry<String, Integer> entry : map.entrySet()) {
       System.out.println(map.getKey() + "    " + map.getValue());      
  }
Now, that is great, but if you try to remove an entry while looping
  for (Map.Entry<String, Integer> entry : map.entrySet()) {
     if(entry.getValue() > 5){
        map.remove(entry.getKey());
      }
}

an java.util.ConcurrentModificationException will occur!
Solving this issue involves adding an Iterator which conforming to documentation “Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.” will allows us to call remove(). Therefore, here it is:
for(Iterator<Map.Entry<String,Integer>>it=map.entrySet().iterator();it.hasNext();){
     Map.Entry<String, Integer> entry = it.next();
     if (entry.getValue() > 5) {
          it.remove();
     }
 }
Done !

 

From http://e-blog-java.blogspot.com/2011/09/removing-entries-from-hashmap.html

Published at DZone with permission of its author, Constantin Alin.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Arnaud Des_vosges replied on Wed, 2011/09/28 - 3:43am

Also a map.remove(key) may be slower than it.remove() because the HashMap must search the entry again.

Carlos Hoces replied on Wed, 2011/09/28 - 3:50am

There must be a typo in your previous example. I guess you meant this:

   for (Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + "    " + entry.getValue());
        }

I tend to prefer this solution:

  final Iterator<Entry<String, Integer>> mapIter = map.entrySet().iterator();
        while (mapIter.hasNext()) {
            final Entry<String, Integer> entry = mapIter.next();
            if (entry.getValue() > 5) {
                mapIter.remove();
            }

I think is more readable, although the results are about the same ones.

andy darlow replied on Wed, 2011/09/28 - 6:56am

I've always preferred the subset approach... Instead of removing, create a subset that matches your requirement. Then no comodification exceptions at all. Something like:
import java.util.HashMap;
import java.util.Map;


public class Maps {

	/**
	 * provides a subset of the map. Entries that match the filter are 
	 * returned.
	 * @param <K> Map's key value type
	 * @param <V> Map's Value value type
	 * @param map the map that you want to filter.
	 * @param filter the filter. Only entries that match the filter are
	 *        returned in the filtered map.
	 * @return subset of the map where the entries match the filter.
	 */
	 public static <K,V> Map<K,V> filter(Map<K,V> map, MapFilter<K,V> filter) {
		Map<K,V> filteredMap = new HashMap<K,V>();
		for (Map.Entry<K, V> entry : map.entrySet()) {
			if (filter.matches(entry))
				filteredMap.put(entry.getKey(), entry.getValue());
		}
		return filteredMap;
	}
}
with:
import java.util.Map;

/**
 *  Filters entries in a map.
 * @param <K> key Type  of Map
 * @param <V> Value Type of map
 */
public interface MapFilter<T,E> {
	
	/**
	 * does this entry match a criteria?
	 * @param mapEntry the entry in a map
	 * @return true if the entry matches the condition, false if not.
	 */
	boolean matches(Map.Entry<T, E> mapEntry);
}
Which will result in code like:

	@Test
	public void testFilter() {
		Map<String, Integer> map = new HashMap<String, Integer>(); 
		map.put("One", 1);
		map.put("Two", 2); 
		map.put("Three", 3);
		map.put("Four", 4); 
		map.put("Five", 5); 
		map.put("Six", 6); 
		map.put("Seven", 7); 
		map.put("Eight", 8); 
		map.put("Nine", 9); 
		map.put("Ten", 10); 
		       
		Map<String, Integer> filteredMap = 
			Maps.filter(map, new MapFilter<String, Integer>() {
			@Override
			public boolean matches(Entry<String, Integer> mapEntry) {
				return mapEntry.getValue() > 5;
			}
		});

		assertEquals(5,filteredMap.size());
		
	}
Of course, don't use this approach if you're tight on memory
Andy

Gergely Pihentagy replied on Wed, 2011/09/28 - 11:12am

It's ugly for me. It's a pity for loop does not give access to the Iterator

andy darlow replied on Wed, 2011/09/28 - 4:22pm in response to: Gergely Pihentagy

which bits are ugly and why? How would you like it to look if you could modify java?

Andy - looking for forward to closures in java........

Jonathan Fisher replied on Wed, 2011/09/28 - 11:25pm

Since everyone here is an expert on this except me :( i'll ask the dumb questions: Isn't map.remove(key) constant time? Why not just call that with your key?

Carlos Hoces replied on Thu, 2011/09/29 - 12:56am in response to: Jonathan Fisher

You cannot do that, while traversing a Map directly, unless you use a ConcurrentHashMap, due to the Concurrent ModificationException thrown: "...if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow this exception." (from the ConcurrentModificationException Javadoc).

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.