【问题标题】:ConcurrentModificationException when using iterator and iterator.remove()使用迭代器和 iterator.remove() 时出现 ConcurrentModificationException
【发布时间】:2012-11-21 11:22:59
【问题描述】:
    private int checkLevel(String bigWord, Collection<String> dict, MinMax minMax)
{
    /*value initialised to losing*/
    int value = 0; 
    if (minMax == MinMax.MIN) value = 1; 
    else value = -1; 


    boolean go = true;

    Iterator<String> iter = dict.iterator();

    while(iter.hasNext())
    {
        String str = iter.next(); 
        Collection<Integer> inds = naiveStringSearch(bigWord, str);

        if(inds.isEmpty())
        {
            iter.remove();
        }

        for (Integer i : inds)
        {
            MinMax passin = minMax.MIN;
            if (minMax == MinMax.MIN) passin = minMax.MAX;

            int value2 = checkLevel(removeWord(bigWord, str, i), dict, passin); 
            if (value2 == -1 && minMax == minMax.MIN)
            {
                value = -1; 
                go = false;
            }
            if (value2 == 1 && minMax == minMax.MAX)
            {
                value = 1; 
                go = false; 
            }

        }

        if (go == false) break; 
    }


    return value;
}

错误:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:810)
at java.util.HashMap$KeyIterator.next(HashMap.java:845)
at aStringGame.Main.checkLevel(Main.java:67)
at aStringGame.Main.test(Main.java:117)
at aStringGame.Main.main(Main.java:137)

这里有什么问题?

【问题讨论】:

  • 你在 checkLevel 做什么?
  • @Nambari - 我更新了代码以显示整个方法。这是一种递归方法。

标签: java collections iterator concurrentmodification


【解决方案1】:

某处正在修改dict。我怀疑它可能发生在这个调用中:

int value2 = checkLevel(removeWord(bigWord, str, i), dict, passin);
                                                     ^^^^

edit 基本上,对checkLevel() 的递归调用通过另一个 迭代器修改dict。这使得外部迭代器的快速失败行为开始发挥作用。

【讨论】:

  • ^我已经更新了问题以表明它是一种递归方法。最好的解决方案是什么?克隆我传入的字典?
  • @jahroy - 但该集合是一个哈希集。 (它是哈希集的原因是我担心性能,我不确定哈希集对于简单的迭代和删除元素是否会很快但是)。
  • 如果你在迭代,所有的集合在性能方面都是相同的。顺便说一句,如果您使用并发集,您将不会遇到此问题。
  • A Set 可以通过确保没有重复来提高性能。如果你从一个 HashSet 创建一个 List,这个 List 也不会有重复。
  • @user1068446 - 如果您担心在 Set 和 List 之间进行转换,请查看我的答案的后半部分。
【解决方案2】:

当您使用迭代器对其进行迭代时,您无法修改集合。

您尝试调用 iter.remove() 违反了此规则(您的 removeWord 方法也可能)。

可以在迭代时修改列表如果您使用ListIterator 进行迭代。

您可以将您的 Set 转换为 List 并使用 List 迭代器:

List<String> tempList = new ArrayList<String>(dict);
ListIterator li = tempList.listIterator();

另一种选择是在迭代时跟踪要删除的元素。

例如,您可以将它们放在一个集合中。

然后您可以在循环之后调用 dict.removeAll()

例子:

Set<String> removeSet = new HashSet<String>();
for (String s : dict) {
    if (shouldRemove(s)) {
        removeSet.add(s);
    }
}
dict.removeAll(removeSet);

【讨论】:

    【解决方案3】:

    当使用for each 循环时,你不能修改Collection 你在循环内迭代。如果需要修改,使用经典的for循环

    【讨论】:

    • 确实,传统的 for 循环会避免 ConcurrentModificationException。但是,您不能通过索引访问 Set 中的元素。
    【解决方案4】:

    这在所有 Collections 类中都很常见。例如 TreeSet 中的条目使用 failfast 方法。

    这个类的迭代器方法返回的迭代器是快速失败的: 如果集合在迭代器创建后的任何时间被修改,在 任何方式,除了通过迭代器自己的 remove 方法,迭代器 将抛出 ConcurrentModificationException。于是,面对 并发修改,迭代器快速而干净地失败, 而不是冒着随意的、非确定性的行为的风险 未来时间未定。

    http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html

    【讨论】:

      猜你喜欢
      • 2013-03-27
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 2013-07-15
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多