33

我知道是否会尝试通过简单的循环从集合中删除,我会得到这个异常: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);
                }
            }
        }
    }
4

4 回答 4

51

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

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

编辑 :

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

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

或者

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

于 2013-06-06T15:01:22.673 回答
2

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

资料来源:谷歌

通过下面编写的示例,您将更好地理解:-

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)
         */
    }
}
于 2017-08-19T07:14:47.580 回答
0

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

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

于 2013-06-06T15:01:55.700 回答
0

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

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
            }
        }
    }
}

JavaIterator旨在“快速失败”,只要它们的底层容器被更改而没有使用Iterator. 您正在使用嵌套迭代器,因此remove()向其中一个发出的任何操作都会导致另一个在Exception继续使用时抛出一个。出于这个原因,如果您需要发出 a remove(),那么您需要在“外部”迭代器(您正在这样做)上执行此操作,然后停止使用第二个迭代器(添加的break语句会这样做)。

于 2013-06-06T15:06:28.877 回答