【问题标题】:java.util.ConcurrentModificationException with iterator带有迭代器的 java.util.ConcurrentModificationException
【发布时间】:2013-06-02 15:52:56
【问题描述】:

我知道是否会尝试通过简单循环从集合中删除,我会得到这个异常:java.util.ConcurrentModificationException。但我正在使用迭代器,它仍然会产生这个异常。知道为什么以及如何解决它吗?

HashSet<TableRecord> tableRecords = new HashSet<>();

...

    for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
        TableRecord record = iterator.next();
        if (record.getDependency() == null) {
            for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
                TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
                if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                    tableRecords.remove(record);
                }
            }
        }
    }

【问题讨论】:

    标签: java iterator removechild concurrentmodification


    【解决方案1】:

    您必须使用iterator.remove() 而不是tableRecords.remove()

    只有在迭代器中使用 remove 方法时,才能删除要迭代的列表中的项目。

    编辑:

    当您创建迭代器时,它会开始计算应用于集合的修改。如果迭代器检测到一些修改没有使用它的方法(或者在同一个集合上使用另一个迭代器),它不能再保证它不会在同一个元素上传递两次或跳过一个,所以它抛出这个异常

    这意味着您需要更改代码,以便仅通过 iterator.remove 删除项目(并且只有一个迭代器)

    列出要删除的项目,然后在完成迭代后将其删除。

    【讨论】:

    • 有两个嵌套的迭代器,所以它可能无法解决问题。
    • @assylias 是的,我没有看到第二个。我对此异常添加了一些解释。
    • 制作要删除的项目列表的第二个选项解决了我的问题。谢谢。
    • 这个解决方案对我有用。我正在使用基于 JSONObject 的 Iterator 以递归方式遍历对象键。当我删除迭代器项时,相关的 JSON 键被删除,这就是我要找的。非常感谢!
    【解决方案2】:

    HashSet 的迭代器的约定是,除了通过特定迭代器的 remove 方法之外,您不能从 hashset 中删除。从dependencyIt 的角度来看,您已经删除了一个项目,而不是调用它的remove 方法,所以它会抛出一个ConcurrentModificationException

    当记录 id 相同时,您似乎想从哈希集中删除记录。覆盖记录的equalshashcode 方法以确保具有相同ID 的记录相等并具有相同的哈希码不是更容易吗? (如果这当然有意义的话)

    【讨论】:

      【解决方案3】:

      问题是您同时在范围内有两个迭代器,它们正在相互“战斗”。解决问题的最简单方法就是在找到匹配项时退出内部循环:

      for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
          TableRecord record = iterator.next();
          if (record.getDependency() == null) {
              for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
                  TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
                  if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                      iterator.remove();
                      break; // ADD THIS LINE
                  }
              }
          }
      }
      

      Java Iterators 旨在“快速失败”,只要其底层容器发生更改而没有使用 Iterator 进行更改。您正在使用嵌套迭代器,因此任何remove() 操作发出给一个将导致另一个Exception 如果继续使用它会抛出一个Exception。出于这个原因,如果您需要发出remove(),那么您需要在“外部”迭代器(您正在这样做)上执行此操作,然后停止使用第二个迭代器(添加了break声明)。

      【讨论】:

        【解决方案4】:

        迭代器快速失败的属性检查任何修改 每次我们尝试获取底层集合的结构 下一个元素。如果发现任何修改,它会抛出 并发修改异常。迭代器的所有实现 在 Collection 类中的设计是快速失败的,除了并发 ConcurrentHashMap 和 CopyOnWriteArrayList 等集合类。

        来源:谷歌

        通过下面的例子你会更好地理解:-

        import java.util.ArrayList;
        import java.util.Arrays;
        import java.util.Iterator;
        import java.util.List;
        
        public class IteratorExp {
            public static void main(String... q) {
                //CASE - ONE
                List<String> strList = new ArrayList<>(Arrays.asList("a", "b", "c"));
                Iterator<String> itr = strList.iterator();
                /*
                 * strList.add("e"); strList.add("f"); strList.add("g");
                 */
                while (itr.hasNext()) {
                    System.out.println(itr.next());
                }
                /*
                 * Exception in thread "main" java.util.ConcurrentModificationException
                 * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
                 * java.util.ArrayList$Itr.next(Unknown Source) at
                 * IteratorExp.main(IteratorExp.java:14)
                 */
                
                //CASE - TWO 
                List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
                Iterator<Integer> itrOne = intList.iterator();
                Iterator<Integer> itrTwo = intList.iterator();
                for (; itrOne.hasNext();) {
                    if (itrOne.next().equals(5)) {
                        itrOne.remove(); // #1
                        //intList.remove(itrOne.next()); // #2
                    }
                }
                for (; itrTwo.hasNext();) {
                    if (itrTwo.next().equals(5)) {
                        itrTwo.remove(); // #1
                        //intList.remove(itrTwo.next()); // #2
                    }
                }
                
                /*
                 * Exception in thread "main" java.util.ConcurrentModificationException
                 * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
                 * java.util.ArrayList$Itr.next(Unknown Source) at
                 * IteratorExp.main(IteratorExp.java:35)
                 */
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2015-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-08
          • 1970-01-01
          • 1970-01-01
          • 2017-06-26
          • 2014-12-13
          相关资源
          最近更新 更多