简体   繁体   English

迭代列表 (ConcurrentModificationException)

[英]Iteration over a list (ConcurrentModificationException)

The following code throws a ConcurrentModificationException:以下代码引发 ConcurrentModificationException:

for (String word : choices) {
         List<String> choicesCopy = choices;
         chosen.add(word);
         choicesCopy.remove(word);
         subsets(choicesCopy, chosen, alreadyPrinted);
}

What's going on?这是怎么回事? The original list (choices) isn't modified at all.原始列表(选项)根本没有被修改。

You made a reference copy not object copy in here您在此处制作了参考副本而不是 object 副本

List<String> choicesCopy = choices;

So obviously you are modifying the same list and you are bound to get the ConcurrentModificationException所以很明显你正在修改同一个列表,你一定会得到ConcurrentModificationException

Use Collections.copy() to properly make a copy of your list.使用Collections.copy()正确复制您的列表。

EDIT: As suggested below you can also use constructor for copying.编辑:如下所示,您还可以使用构造函数进行复制。

The reason is because you cannot modify anything inside a foreach loop.原因是您不能在 foreach 循环中修改任何内容。 Try using a for loop.尝试使用 for 循环。 Or you have take all the contents of list and add them 1 at a time to the other list.或者您已获取列表的所有内容并将它们一次添加 1 到另一个列表。 because its done by reference因为它是通过引用完成的

Edit: You need to make a deep copy of the list and remove from that copy.编辑:您需要制作列表的深层副本并从该副本中删除。 Then you can assign the reference of the original list to point to the new one that has the modifications.然后,您可以将原始列表的引用指定为指向具有修改的新列表。 You cannot remove from the list you're currently iterating through even if it's being referenced by another variable.即使它被另一个变量引用,您也无法从当前迭代的列表中删除。

Change the code like this:像这样更改代码:

for (Iterator<String> it = choices.iterator(); it.hasnext();) {
     String word = it.next();
     chosen.add(word);
     it.remove();
     subsets(choicesCopy, chosen, alreadyPrinted);
}

Explanation: foreach loops use an iterator internally, but don't expose it to the user.说明: foreach 循环在内部使用迭代器,但不要将其暴露给用户。 So if you want to remove items you have to simulate the foreach loop and keep a reference to the iterator yourself.因此,如果要删除项目,则必须模拟 foreach 循环并自己保留对迭代器的引用。

While iterating, any other means of removing data from a collection will result in a ConcurrentModificationException .迭代时,任何其他从集合中删除数据的方法都将导致ConcurrentModificationException

I think the universal solution is:我认为通用的解决方案是:

List<E> obj = Collections.synchronizedList(new ArrayList<E>());

You'll need to copy the list properly eg Collections.copy and then remove from the copy, or use Iterator.remove, which will remove the Object from the underlying collection.您需要正确复制列表,例如 Collections.copy,然后从副本中删除,或使用 Iterator.remove,这将从基础集合中删除 Object。 Iterators are fail fast, so you can't change the underlying Collection without using the API of the Iterator.迭代器很快就会失败,因此如果不使用迭代器的 API,就无法更改底层集合。

I suspect chosen should be a copy as well.我怀疑 selected 也应该是副本。 Otherwise chosen will accumulates all the words by the time the loop has finished.否则选择将在循环完成时累积所有单词。 ie I suspect the chosen and choices shouldn't have any words in common.即我怀疑选择和选择不应该有任何共同点。

I also suspect the collections should be sets (unordered collections without duplicates) instead of lists.我还怀疑应该设置 collections (无重复的无序 collections)而不是列表。

Perhaps the code should be.也许代码应该是。

Set<String> choices =
Set<String> chosen =
for (String word : choices) {
     Set<String> choicesCopy = new LinkedHashSet<String>(choices);
     choicesCopy.remove(word);
     Set<String> chosenCopy = new LinkedHashSet<String>(chosen);
     chosenCopy.add(word);

     subsets(choicesCopy, chosenCopy, alreadyPrinted);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM