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

时间:2018-08-02 13:44:26

标签: java loops exception concurrentmodification

我有ConcurrentModificationException,不知道为什么。我知道尝试使用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));
    }

2 个答案:

答案 0 :(得分:0)

问题在于您对subList()的呼叫不会创建新列表。正如javadoc所说:

  

返回此列表部分在指定的fromIndex(包括)和toIndex(不包括)之间的视图

javadoc还说:

  

如果后备列表(即此列表)以除通过返回列表以外的任何其他方式进行了结构修改,则此方法返回的列表的语义将变得不确定。

调用partitions.get(i).add(...)时,您正在结构上修改 originalList,从而导致错误。

我不认为您打算这样做,因此要解决此问题,您只需确保子列表独立于原始列表(即副本)即可,这很容易做到:

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

使用ArrayList(Collection)构造函数将创建子列表的副本。

因此,请更改以下语句:

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

对此:

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

答案 1 :(得分:-2)

绝对不要迭代列表并执行更新操作(更新意味着添加或删除元素)。这是灾难的秘诀。

为了解决此问题,需要遵循三种可能的方案:

1)复制列表,遍历副本,然后从原始副本中删除。

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

2)使用流

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

3)使用迭代器遍历列表

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

我个人更喜欢流。