为什么 List.add 列表的反转子列表的所有都会导致 ConcurrentModificationException

我一直在尝试获取列表的子列表,将其反转,然后将反向列表放回起始位置。例如,假设我们有列表,然后从索引 2 反转到索引 4 将得到 。[1, 2, 3, 4, 5, 6][1, 2, 5, 4, 3, 6]

我为此编写了一些代码,但是它每次都给出一个(除非startIndex == endIndex)。下面提供了一个最小的可重现性示例:ConcurrentModificationException

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

线程 “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);

我不确定问题是什么,因为在迭代时似乎没有任何东西被改变。如果有人可以解释为什么会发生这种情况和/或如何解决它,那将不胜感激。


答案 1

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

list.subList(startIndex, endIndex+1)

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

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

答案 2

来自 ArrayList.subList 的文档

返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然

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