我正在做:

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

它抛出了一个ConcurrentModificationException,所以我把它改为:

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

这个过程以及修改映射的任何其他过程都在同步块中。

有没有更好的解决方案?


当前回答

有没有更好的解决方案?

当然,在单个语句中有更好的方法,但这取决于基于删除哪些元素的条件。

例如:删除所有值为test的元素,然后使用下面的代码:

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

更新 这可以在一行中使用Java 8中的Lambda表达式完成。

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

我知道这个问题太老了,但更新更好的做事方式并没有什么坏处:)

其他回答

另一种更详细的方法

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

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

也许您可以遍历映射,寻找要删除的键,并将它们存储在单独的集合中。然后从映射中删除键集合。在迭代过程中修改地图通常是不受欢迎的。如果地图非常大,这个想法可能是可疑的。

使用真正的迭代器。

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

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

实际上,您可能需要遍历entrySet()而不是keySet()才能使其工作。

有没有更好的解决方案?

当然,在单个语句中有更好的方法,但这取决于基于删除哪些元素的条件。

例如:删除所有值为test的元素,然后使用下面的代码:

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

更新 这可以在一行中使用Java 8中的Lambda表达式完成。

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

我知道这个问题太老了,但更新更好的做事方式并没有什么坏处:)

Java 8支持一种更具声明性的迭代方法,我们指定我们想要的结果,而不是如何计算它。这种新方法的好处是可读性更强,更不容易出错。

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

结果如下:

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