Why do I get a ConcurentModificationException when removing from a HashMap?
I want to remove an item from HashMap, by applying a criteria. Consider this code:
Set<Foo> set = myMap.keySet();
Iterator<Foo> itr = set.iterator();
while (itr.hasNext())
{
Foo foo = itr.next();
if (foo.toString().length() < 3) {
myMap.remove(foo); //remove the pair if key length is less than 3
}
}
So I get a ConcurentModificationException because during开发者_如何学C the iteration I am modifying the HashMap. What should I do? Is there any other way to search for my criteria and execute the remove command at the end so that I can avoid this exception?
Use itr.remove()
instead of myMap.remove(o.toString())
As of Java 8, Collection provides removeIf(Predicate<? super E>)
, which will remove all elements for which the given predicate returns true. The example in the question could be rewritten as
myMap.keySet().removeIf(o -> o.toString().length() < 3);
The default implementation provided by Collection uses an iterator and calls Iterator.remove
, but collections can override this if they can provide better implementations. More importantly, code using removeIf
is clearer and more concise.
If you are removing element during iteration you have to use Iterator.remove() instead. Otherwise the current Iterator object enters inconsistent state that causes the exception. You can use Map.remove(key) when you know key, i.e. when you are not iterating over the same Map.
This rule is correct for all collections (Lists, Sets etc).
Yes - itr.remove()
Removes from the underlying collection the last element returned by the iterator (optional operation). This method can be called only once per call to next.
The Iterator
of the keySet()
extends the HashIterator
, whose remove()
method calls HashMap.this.removeEntryForKey(key);
You can also obtain the entrySet()
if you need both the key and the value - its iterator has the same property.
To do what you describe I personally like to use functional style programming:
Map<String,Object> map = // obtained somehow;
Map<String,Object> filtered = Maps.filterKeys(map, new Predicate() {
@Override
public boolean apply(String input) {
return input.length() < 3;
}
});
The code snippet uses the google collections library. It creates a view of the original map by taking only those entries which keys match the supplied predicate.
精彩评论