簡體   English   中英

無法弄清楚為什么會引發ConcurrentModificationException

[英]Cannot figure out why it throws ConcurrentModificationException

我有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));
    }

問題在於您對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)復制列表,遍歷副本,然后從原始列表中刪除。

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();
    }
}

我個人更喜歡流。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM