iterating over and removing from a map

Java

Java Problem Overview


I was doing:

for (Object key : map.keySet())
    if (something)
        map.remove(key);

which threw a ConcurrentModificationException, so i changed it to:

for (Object key : new ArrayList<Object>(map.keySet()))
    if (something)
        map.remove(key);

this, and any other procedures that modify the map are in synchronized blocks.

is there a better solution?

Java Solutions


Solution 1 - Java

Here is a code sample to use the iterator in a for loop to remove the entry.

Map<String, String> map = new HashMap<String, String>() {
  {
    put("test", "test123");
    put("test2", "test456");
  }
};
    
for(Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); it.hasNext(); ) {
    Map.Entry<String, String> entry = it.next();
    if(entry.getKey().equals("test")) {
        it.remove();
    }
}

Solution 2 - Java

As of Java 8 you could do this as follows:

map.entrySet().removeIf(e -> <boolean expression>);

Oracle Docs: entrySet() > The set is backed by the map, so changes to the map are reflected in the set, and vice-versa

Solution 3 - Java

Use a real iterator.

Iterator<Object> it = map.keySet().iterator();

while (it.hasNext())
{
  it.next();
  if (something)
    it.remove();
 }

Actually, you might need to iterate over the entrySet() instead of the keySet() to make that work.

Solution 4 - Java

> is there a better solution?

Well, there is, definitely, a better way to do so in a single statement, but that depends on the condition based on which elements are removed.

For eg: remove all those elements where value is test, then use below:

map.values().removeAll(Collections.singleton("test"));

UPDATE It can be done in a single line using Lambda expression in Java 8.

map.entrySet().removeIf(e-> <boolean expression> );

I know this question is way too old, but there isn't any harm in updating the better way to do the things :)

Solution 5 - Java

ConcurrentHashMap

You can use java.util.concurrent.ConcurrentHashMap.

It implements ConcurrentMap (which extends the Map interface).

E.g.:

Map<Object, Content> map = new ConcurrentHashMap<Object, Content>();

for (Object key : map.keySet()) {
    if (something) {
        map.remove(key);
    }
}

This approach leaves your code untouched. Only the map type differs.

Solution 6 - Java

Java 8 support a more declarative approach to iteration, in that we specify the result we want rather than how to compute it. Benefits of the new approach are that it can be more readable, less error prone.

public static void mapRemove() {

	Map<Integer, String> map = new HashMap<Integer, String>() {
		{
			put(1, "one");
			put(2, "two");
			put(3, "three");
		}
	};

	map.forEach( (key, value) -> { 
        System.out.println( "Key: " + key + "\t" + " Value: " + value );  
    });	

	map.keySet().removeIf(e->(e>2)); // <-- remove here

	System.out.println("After removing element");

	map.forEach( (key, value) -> { 
        System.out.println( "Key: " + key + "\t" + " Value: " + value ); 
    });
}

And result is as follows:

Key: 1	 Value: one
Key: 2	 Value: two
Key: 3	 Value: three
After removing element
Key: 1	 Value: one
Key: 2	 Value: two

Solution 7 - Java

You have to use Iterator to safely remove element while traversing a map.

Solution 8 - Java

I agree with Paul Tomblin. I usually use the keyset's iterator, and then base my condition off the value for that key:

Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
    Integer key = it.next();
    Object val = map.get(key);
    if (val.shouldBeRemoved()) {
        it.remove();
    }
}

Solution 9 - Java

An alternative, more verbose way

List<SomeObject> toRemove = new ArrayList<SomeObject>();
for (SomeObject key: map.keySet()) {
    if (something) {
        toRemove.add(key);
    }
}

for (SomeObject key: toRemove) {
    map.remove(key);
}

Solution 10 - Java

And this should work as well..

ConcurrentMap<Integer, String> running = ... create and populate map

Set<Entry<Integer, String>> set = running.entrySet();    

for (Entry<Integer, String> entry : set)
{ 
  if (entry.getKey()>600000)
  {
    set.remove(entry.getKey());    
  }
}

Solution 11 - Java

Maybe you can iterate over the map looking for the keys to remove and storing them in a separate collection. Then remove the collection of keys from the map. Modifying the map while iterating is usually frowned upon. This idea may be suspect if the map is very large.

Solution 12 - Java

Set s=map.entrySet();
Iterator iter = s.iterator();

while (iter.hasNext()) {
    Map.Entry entry =(Map.Entry)iter.next();
    
    if("value you need to remove".equals(entry.getKey())) {
         map.remove();
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpstantonView Question on Stackoverflow
Solution 1 - JavaGennadiyView Answer on Stackoverflow
Solution 2 - JavaeaorakView Answer on Stackoverflow
Solution 3 - JavaPaul TomblinView Answer on Stackoverflow
Solution 4 - JavaShishir KumarView Answer on Stackoverflow
Solution 5 - JavaROMANIA_engineerView Answer on Stackoverflow
Solution 6 - JavaVineet kaushikView Answer on Stackoverflow
Solution 7 - JavaAlexander PogrebnyakView Answer on Stackoverflow
Solution 8 - JavagoatlinksView Answer on Stackoverflow
Solution 9 - JavavickirkView Answer on Stackoverflow
Solution 10 - JavaAftershockView Answer on Stackoverflow
Solution 11 - JavaVincent RamdhanieView Answer on Stackoverflow
Solution 12 - JavaCrotic ParadoxView Answer on Stackoverflow