简体   繁体   English

无法弄清楚为什么会引发ConcurrentModificationException

[英]Cannot figure out why it throws ConcurrentModificationException

I've got the ConcurrentModificationException and do not know why. 我有ConcurrentModificationException,不知道为什么。 I know that trying to iterate through a list using for loops and deleting elements inside the loop block is bad idea and can throw such exception, but I have no idea how to fix it in my case. 我知道尝试使用for循环遍历列表并删除循环块内的元素是一个坏主意,并且可能引发此类异常,但是我不知道如何解决此问题。

 private static final List<Integer> originalList = new ArrayList<>();

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            originalList.add(i);
        }


        final int MAX_GROUP_SIZE = 5;
        int partitionSize = 4;

        List<List<Integer>> partitions = new LinkedList<>();

        for (int i = 0; i < originalList.size(); i += partitionSize) {
            partitions.add(originalList.subList(i,
                    Math.min(i + partitionSize, originalList.size())));
        }

        int lastGroupSize = partitions.get(partitions.size() - 1).size();

        if (lastGroupSize < partitionSize && partitions.size() > lastGroupSize){
            List<Integer> lastGroup = partitions.remove(partitions.size() - 1);
            for (int i = 0; i < lastGroupSize; i++) {
                partitions.get(i).add(lastGroup.get(i));
            }
        }
        System.out.println("GROUPS: " + partitions.size());
        printGroups(new LinkedList<>(partitions));
    }

The problem is that your calls to subList() don't create new lists. 问题在于您对subList()调用不会创建新列表。 As the javadoc says it: 正如javadoc所说:

Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. 返回此列表中指定的fromIndex(包括)和toIndex(不包括)之间的视图

The javadoc also says: Javadoc还说:

The semantics of the list returned by this method become undefined if the backing list (ie, this list) is structurally modified in any way other than via the returned list. 如果后备列表(即此列表)以结构方式(而不是通过返回的列表)进行了修改 ,则此方法返回的列表的语义将变得不确定。

When you call partitions.get(i).add(...) , you're structurally modifying originalList , causing the error. 当您调用partitions.get(i).add(...) ,您正在结构上修改 originalList ,从而导致错误。

I don't believe you intended that, so to fix the problem, you just need to make sure your sub-lists are independent of the original list, ie copies, which is easy to do: 我不认为您打算这样做,因此要解决此问题,您只需要确保子列表独立于原始列表(即副本)即可,这很容易做到:

new ArrayList<>(originalList.subList(...))

Using the ArrayList(Collection) constructor will create a copy of the sub-list. 使用ArrayList(Collection)构造函数将创建子列表的副本。

So, change this statement: 因此,更改此语句:

partitions.add(originalList.subList(i,
        Math.min(i + partitionSize, originalList.size())));

to this: 对此:

partitions.add(new ArrayList<>(originalList.subList(i,
        Math.min(i + partitionSize, originalList.size()))));

You should never iterate the list and perform updating actions while doing so (where updating means adding or removing elements). 绝对不要迭代列表并执行更新操作(更新意味着添加或删除元素)。 This is a recipe for disaster. 这是灾难的秘诀。

In order to resolve this there are three possible scenarios to follow: 为了解决此问题,有以下三种可能的方案:

1) Copy the list, iterate over the copy and remove from the original one. 1)复制列表,遍历副本,然后从原始列表中删除。

for (var number : new ArrayList<>(original)) {
    if (element > 10) {
        original.remove(element);
    }
}

2) Use Streams 2)使用流

List<Integer> filtered = original.stream()
                                 .filter(i -> i > 10)
                                 .collect(Collectors.toList());

3) Use an iterator to loop over the list 3)使用迭代器遍历列表

Iterator<Integer> iterator = original.iterator();
while (iterator.hasNex()) {
    Integer number = iterator.next();
    if (number > 10) {
        iterator.remove();
    }
}

Personally I prefer streams. 我个人更喜欢流。

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

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