此问题已经在这里有了答案:
我一直在尝试获取列表的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);
线程“主”中的异常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);
。
我不确定是什么问题,因为迭代过程中似乎没有任何变化。如果有人可以解释why的情况和/或how进行修复,将不胜感激。
List.subList返回指定元素之间列表的实时视图,而不是这些元素的副本(请参见documentation),因此,将其添加到原始列表还将修改子列表,从而导致ConcurrentModificationException
(因为添加的内容和添加的内容也会同时被修改)。
list.subList(startIndex, endIndex+1)
您可以通过复制列表来修复代码,例如
List<Integer> toReverse = new ArrayList<>(list.subList(startIndex, endIndex+1));
根据helospark和Nir Levy的建议,在流中使用skip & limit
List<Integer> toReverse = list.stream() //
.skip(startIndex) //
.limit(endIndex + 1) //
.collect(Collectors.toList());
问题在这里位于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);