遍历映射并从映射中移除

我正在做:

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

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

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

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

有没有更好的解决方案?

291963 次浏览

使用真正的迭代器。

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


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

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

在遍历映射时,必须使用Iterator来安全地删除元素。

下面是一个在for循环中使用迭代器删除条目的代码示例。

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

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

另一种更详细的方法

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


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

我同意Paul Tomblin的观点。我通常使用键集的迭代器,然后以该键的值为条件:

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

有没有更好的解决方案?

当然,在条语句中肯定有更好的方法来这样做,但这取决于基于哪些元素被删除的条件。

例如:删除所有value测验的元素,然后使用如下语句:

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

<强>更新 它可以在Java 8中使用Lambda表达式在单行中完成

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

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

这也应该可以工作。

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

ConcurrentHashMap

你可以使用java.util.concurrent.ConcurrentHashMap

它实现了ConcurrentMap(扩展了Map接口)。

如。:

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


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

这种方法不会影响代码。只有map类型不同。

从Java 8开始,你可以这样做:

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

Oracle Docs: entrySet()

集合是由映射支持的,所以对映射的更改会反映在集合中,反之亦然

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

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