为什么列表的反向子列表的List.addAll导致ConcurrentModificationException

时间:2019-09-30 10:07:24

标签: java arraylist collections concurrentmodification

我一直在尝试获取列表的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);
  

线程“ main”中的异常java.util.ConcurrentModificationException
    在java.util.ArrayList $ SubList.checkForComodification(未知来源)
    在java.util.ArrayList $ SubList.size(未知来源)在
  java.util.AbstractCollection.toArray(未知源),位于
  java.util.ArrayList.addAll(未知来源),位于
  test.ConcurrentExample.main(ConcurrentExample.java:64)

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

我不确定问题是什么,因为迭代过程中似乎没有任何变化。如果有人能解释为什么为什么和/或如何解决,将不胜感激。

4 个答案:

答案 0 :(得分:11)

List.subList返回指定元素之间列表的实时视图,而不是这些元素的副本(请参见documentation),因此,将其添加到原始列表还将修改子列表,从而导致{ {1}}(因为添加的内容和添加的内容也同时被修改)。

ConcurrentModificationException

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

list.subList(startIndex, endIndex+1)

答案 1 :(得分:5)

摘自ArrayList.subList的文档:

  

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

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

答案 2 :(得分:2)

问题位于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);

答案 3 :(得分:0)

根据 helospark Nir ​​Levy 的建议,在流中使用skip & limit

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