【问题标题】:ConcurrentModificationException - checkForComodification(ArrayList.java)ConcurrentModificationException - checkForComodification(ArrayList.java)
【发布时间】:2014-10-21 05:22:31
【问题描述】:

对于下面的带有 Hash Map 的 java 程序,抛出 ConcurrentModification 异常,我已经在程序中标记了抛出异常的行。

我暂时跳过了向 HashMap 中插入数据的登录

import java.util.ArrayList;
 import java.util.HashMap;

 public class MainClass {

     public static void main(String[] args) {

         ArrayList<HashMap<String, ArrayList<String>>> arrMain = new ArrayList<HashMap<String, ArrayList<String>>>();

         HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();

         ArrayList<String> strings = new ArrayList<String>();

         // Code to build the above Maps with all required Data, Skipped for Now

         //******************Scenario 1****************
         for (HashMap<String, ArrayList<String>> dataMap : arrMain) { //ConcurrentModification Exception
             for (String s : dataMap.get("Key")) {
                 ArrayList<String> newStrings = new ArrayList<String>();
                 newStrings.addAll(dataMap.get("Key")); 
                 newStrings.add("New String");
                 dataMap.put("Key", newStrings);
             }

         }

         //******************Scenario 2****************
         for (HashMap<String, ArrayList<String>> dataMap : arrMain) {//ConcurrentModification Exception
             for (String s : dataMap.get("Key")) {
                 dataMap.get("Key").add("New String"); 
             }

         }

     }

 }

错误:

java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
    at java.util.ArrayList$Itr.next(ArrayList.java:791)

【问题讨论】:

  • 你不能遍历 hashmap 并同时使用 addall 或 add。这就像同时读取和更改位置。

标签: java arraylist hashmap


【解决方案1】:

当您尝试在迭代时修改集合时,您将得到ConcurrentModificationException。您可以尝试使用Iterator 来避免这种情况。

例如:IteratorMap

    Map<String, String> map = new HashMap<>();
    map.put("a", "a1");
    map.put("b", "b1");
    map.put("c", "c1");
    System.out.println(map);
    Iterator it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry pairs = (Map.Entry) it.next();
        System.out.println(pairs.getKey() + " = " + pairs.getValue());
        it.remove(); // no a ConcurrentModificationException
    }
    System.out.println(map);

输出:

{b=b1, c=c1, a=a1}
b = b1
c = c1
a = a1
{}

【讨论】:

    【解决方案2】:

    您在代码中标记了 2 个ConcurrentModificationException 点,但只有一个点是可重现且合理的。

    第二个:

    for (HashMap<String, ArrayList<String>> dataMap : arrMain) {
         for (String s : dataMap.get("Key")) { // HERE 
             dataMap.get("Key").add("New String"); 
         }
    }
    

    dataMap.get("Key") 返回一个 ArrayList,在 for 循环中添加另一个元素:

    dataMap.get("Key").add("New String");
    

    在添加元素(修改它)之后,for 循环将继续。在迭代它的中间添加一个元素到列表中。这会导致ConcurrentModificationException(在底层,增强的for 使用列表的迭代器来遍历其元素,如果列表在创建迭代器后被修改,则迭代器next() 方法会抛出此异常)。

    代码中您指出ConcurrentModificationException 的第一个点,它不会导致异常,因为您遍历一个列表并修改另一个列表,即您在for 循环中创建的新列表。但我怀疑这是你真正想要做的。

    【讨论】:

      【解决方案3】:

      您可以使用ConcurrentHashMap

      支持检索的完全并发和可调整的预期更新并发的哈希表。

      该类遵循与 Hashtable 相同的功能规范,并包含与 Hashtable 的每个方法对应的方法版本。

      但是,尽管所有操作都是线程安全的,但检索操作并不需要锁定,并且不支持以阻止所有访问的方式锁定整个表。

      在依赖线程安全但不依赖同步细节的程序中,此类与 Hashtable 完全可互操作。


      您可以使用迭代器。 迭代器被设计为一次只能由一个线程使用。它们不会抛出 ConcurrentModificationException。

      ConcurrentModificationException

      public class ConcurrentModificationException
                   extends RuntimeException
      

      当这种修改是不允许的时,检测到对象的并发修改的方法可能会抛出此异常。

      例如,通常不允许一个线程在另一个线程对其进行迭代时修改 Collection。

      另见

      【讨论】:

        猜你喜欢
        • 2012-10-12
        • 2015-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多