关于java:迭代和从地图中删除

iterating over and removing from a map

本问题已经有最佳答案,请猛点这里访问。

我在做:

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

它引发了一个ConcurrentModificationException,所以我把它改为:

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

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

有更好的解决方案吗?

如果没有人提出更好的解决方案,首先要说"不",然后打勾;)


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
    }
}


至于Java 8,你可以这样做:

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


使用真正的迭代器。

1
2
3
4
5
6
7
8
Iterator<Object> it = map.keySet().iterator();

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

实际上,您可能需要迭代entrySet(),而不是keySet(),以使其工作。


is there a better solution?

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

例如:删除所有测试value的元素,然后使用以下内容:

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

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

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

我知道这个问题太老了,但是更新更好的方法并没有什么害处。


当前地图

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

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

例如。:

1
2
3
4
5
6
7
Map<Object, Content> map = new ConcurrentHashMap<Object, Content>();

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

这种方法不会影响您的代码。只有Map型不同。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 );
    });
}

结果如下:

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


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


我同意保罗·汤布林的观点。我通常使用keyset的迭代器,然后根据该键的值确定条件:

1
2
3
4
5
6
7
8
Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
    Integer key = it.next();
    Object val = map.get(key);
    if (val.shouldBeRemoved()) {
        it.remove();
    }
}


另一种更详细的方法

1
2
3
4
5
6
7
8
9
10
List<SomeObject> toRemove = new ArrayList<SomeObject>();
for (SomeObject key: map.keySet()) {
    if (something) {
        toRemove.add(key);
    }
}

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

这也应该有效。

1
2
3
4
5
6
7
8
9
10
11
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);    
  }
}

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


1
2
3
4
5
6
7
8
9
10
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();
    }
}