迭代地图并更改值时如何避免ConcurrentModificationException?

Jim*_*mmy 19 java loops map

我有一个包含一些键(字符串)和值(POJO)的映射

我想迭代这个地图并改变POJO中的一些数据.

我继承的当前代码删除了给定的条目,并在对POJO进行一些更改后将其添加回来.

这不能很好地工作,因为在迭代它时你不应该修改地图(方法是同步的,但仍然出现ConcurrentModificationException)

我的问题是,如果我需要遍历地图并更改值,我可以使用哪些最佳实践/方法?要创建一个单独的地图并按照我的方式构建,然后返回副本?

T.J*_*der 22

两种选择:

选项1

我继承的当前代码删除了给定的条目,并在对POJO进行一些更改后将其添加回来.

您是否正在更改对POJO 的引用?例如,入口指向其他东西完全?因为如果没有,根本不需要从地图上删除它,你可以改变它.

选项2

如果确实需要实际更改对POJO的引用(例如,条目的值),您仍然可以通过迭代Map.Entry实例来实现entrySet().您可以setValue在条目上使用,这不会修改您要迭代的内容.

例:

Map<String,String>                  map;
Map.Entry<String,String>            entry;
Iterator<Map.Entry<String,String>>  it;

// Create the map
map = new HashMap<String,String>();
map.put("one", "uno");
map.put("two", "due");
map.put("three", "tre");

// Iterate through the entries, changing one of them
it = map.entrySet().iterator();
while (it.hasNext())
{
    entry = it.next();
    System.out.println("Visiting " + entry.getKey());
    if (entry.getKey().equals("two"))
    {
        System.out.println("Modifying it");
        entry.setValue("DUE");
    }
}

// Show the result
it = map.entrySet().iterator();
while (it.hasNext())
{
    entry = it.next();
    System.out.println(entry.getKey() + "=" + entry.getValue());
}
Run Code Online (Sandbox Code Playgroud)

输出(无特定顺序)是:

访问两个
修改它
访问一个
访问三个
两个= DUE
一个= uno
三个= tre

......没有任何修改例外.你可能想要同步它,以防其他东西也在查看/混淆该条目.


Ste*_*n C 15

迭代a Map并同时添加条目将导致ConcurrentModificationException大多数Map类.对于Map没有(例如ConcurrentHashMap)的类,不能保证迭代将访问所有条目.

根据您正在做的事情,您可以在迭代时执行以下操作:

  • 使用该Iterator.remove()方法删除当前条目,或
  • 使用该Map.Entry.setValue()方法修改当前条目的值.

对于其他类型的更改,您可能需要:

  • Map从当前的条目创建一个新的Map,或
  • 构建一个单独的数据结构,包含要进行的更改,然后应用于Map.

最后,Google Collections和Apache Commons Collections库具有用于"转换"地图的实用程序类.


Sea*_*oyd 8

出于此目的,您应该使用地图公开的集合视图:

  • keySet()允许您遍历键.这对你没有帮助,因为键通常是不可变的.
  • 如果您只想访问地图值,则需要values().如果它们是可变对象,您可以直接更改,无需将它们放回到地图中.
  • entrySet()是最强大的版本,允许您更改条目的键和/或值.

示例:将包含upperscore的所有键的值转换为大写

for(Map.Entry<String, String> entry:map.entrySet()){
    if(entry.getKey().contains("_"))
        entry.setValue(entry.getValue().toUpperCase());
}
Run Code Online (Sandbox Code Playgroud)

实际上,如果您只想编辑值对象,请使用values集合进行编辑.我假设您的地图是类型<String, Object>:

for(Object o: map.values()){
    if(o instanceof MyBean){
        ((Mybean)o).doStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)