有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么要列出。添加列表的所有反转子列表会导致ConcurrentModificationException

我一直在尝试从一个列表中取一个sub list,将其反转,然后将reversed列表放回起始位置。例如,假设我们有一个列表[1, 2, 3, 4, 5, 6],那么从索引2反转到索引4将得到[1, 2, 5, 4, 3, 6]

我已经为此编写了一些代码,但是每次都会给出一个ConcurrentModificationException(除非startIndex==endIndex)。下面提供了一个最小可复制示例:

int startIndex = 2;
int endIndex = 4;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);

List<Integer> toReverse = list.subList(startIndex, endIndex+1);
Collections.reverse(toReverse);
list.removeAll(toReverse);
list.addAll(startIndex, toReverse);

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(Unknown Source)
at java.util.ArrayList$SubList.size(Unknown Source) at
java.util.AbstractCollection.toArray(Unknown Source) at
java.util.ArrayList.addAll(Unknown Source) at
test.ConcurrentExample.main(ConcurrentExample.java:64)

错误所指的实际行是list.addAll(startIndex, toReverse);

我不确定问题是什么,因为在迭代过程中似乎没有任何内容得到更改。如果有人能解释为什么会发生这种情况和/或如何解决它,我们将不胜感激


共 (4) 个答案

  1. # 1 楼答案

    问题在这里ArrayList#checkForComodification

    private void checkForComodification() {
        if (ArrayList.this.modCount != this.modCount)
            throw new ConcurrentModificationException();
        }
    }
    

    但是,在这种特殊情况下,您不需要手动重新添加反转子列表,因为反转是在原始列表上执行的。所以你所需要的就是放弃

    list.removeAll(...);
    list.addAll(...);
    

    只保留以下代码:

    List<Integer> toReverse = list.subList(startIndex, endIndex+1);
    Collections.reverse(toReverse);
    
  2. # 2 楼答案

    来自helospark的建议<强流{a1^>正在使用

    List<Integer> toReverse = list.stream() //
                    .skip(startIndex) //
                    .limit(endIndex + 1) //
                    .collect(Collectors.toList());
    
  3. # 3 楼答案

    ArrayList.subList的文件中:

    The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa

    因此,当您尝试在子列表“视图”的索引处添加项时,它会创建并发修改

  4. # 4 楼答案

    名单。子列表返回指定元素之间列表的实时视图,而不是这些元素的副本(请参见documentation),因此添加到原始列表也会修改子列表,这将导致ConcurrentModificationException(因为添加的内容和添加到的内容也会同时被修改)

    list.subList(startIndex, endIndex+1)
    

    您可以通过复制列表来修复代码,如

    List<Integer> toReverse = new ArrayList<>(list.subList(startIndex, endIndex+1));