为什么从HashMap中删除元素时会出现ConcurrentModificationException?

3
我希望能够通过应用某些条件从HashMap中删除一个条目。请考虑以下代码:
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
    }
}

我因为在迭代过程中修改了HashMap而得到了ConcurrentModificationException异常。我该怎么办?有没有其他方法可以搜索我的条件并在最后执行删除命令,以避免这个异常?


如果我使用try/catch块,在运行时程序会在某个地方卡住,光标会一直闪烁。我必须通过CTR+C终止程序。 有没有什么办法可以避免这种情况,仍然能够从HashMap中删除键/值对? - Mavin
5个回答

13

请使用itr.remove()代替myMap.remove(o.toString())


你确定这个能行吗?问题中的迭代器不是HashMap的迭代器,而是HashMap键集的迭代器。 - sje397
实际上,您可以在keySet迭代器上使用remove()方法来删除键值映射:请参见http://download.oracle.com/javase/1.4.2/docs/api/java/util/Map.html#keySet(%29 - Christian Semrau
是的,我确定。删除该键将删除整个条目。 - user330315
@Thorbjørn Ravn Andersen - 我相信你是在说o.toString()是错误的。对吗?你是对的,但这并不是OP目前问题的原因。 - Stephen C

4

从Java 8开始,Collection提供了removeIf(Predicate<? super E>),它将删除给定谓词返回true的所有元素。问题中的示例可以重写为

myMap.keySet().removeIf(o -> o.toString().length() < 3);

集合提供的默认实现使用迭代器并调用 Iterator.remove,但是如果集合可以提供更好的实现,则可以重写它。更重要的是,使用removeIf的代码更加清晰简洁。


3

如果你在迭代过程中删除元素,必须使用Iterator.remove()。否则当前的Iterator对象会进入不一致状态,导致异常。当你不在迭代同一个Map时,可以使用Map.remove(key)来删除已知key的元素。

对于所有集合(列表、集合等),此规则都是正确的。


1

是的 - itr.remove()

从迭代器返回的集合中删除最后一个元素(可选操作)。此方法每次调用next只能调用一次。

keySet()Iterator扩展了HashIterator,其remove()方法调用HashMap.this.removeEntryForKey(key);

如果您需要键和值,则还可以获取entrySet() - 其迭代器具有相同的属性。


0

要实现您所描述的功能,我个人喜欢使用函数式编程风格:

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;
    }
});

代码片段使用了Google Collections库。它通过仅获取键与提供的谓词匹配的条目来创建原始地图的视图。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接