java arraylist collections concurrentmodification

java - ¿Por qué List.addAll de una sublista invertida de la lista causa una ConcurrentModificationException



arraylist collections (4)

He estado tratando de tomar una sublista de una lista, invertirla y colocar la lista reversed nuevamente en la posición inicial. Por ejemplo, supongamos que tenemos la lista [1, 2, 3, 4, 5, 6] , luego, al revertir del índice 2 al índice 4, obtendría [1, 2, 5, 4, 3, 6] .

He escrito un código para esto, sin embargo, proporciona una ConcurrentModificationException cada vez (a menos que startIndex == endIndex). A continuación se proporciona un ejemplo reproducible mínimo:

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

Excepción en el hilo "main" java.util.ConcurrentModificationException
en java.util.ArrayList $ SubList.checkForComodification (Fuente desconocida)
en java.util.ArrayList $ SubList.size (Fuente desconocida) en
java.util.AbstractCollection.toArray (fuente desconocida) en
java.util.ArrayList.addAll (fuente desconocida) en
test.ConcurrentExample.main (ConcurrentExample.java:64)

La línea real a la que se refiere el error es list.addAll(startIndex, toReverse); .

No estoy seguro de cuál es el problema, ya que nada parece estar cambiando mientras se repite. Si alguien pudiera explicar por qué sucede esto y / o cómo solucionarlo, sería muy apreciado.


A partir de sugerencias de helospark y Nir Levy , use omitir y limitar en Stream

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


El problema se encuentra aquí en ArrayList#checkForComodification

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

Sin embargo, en este caso particular, no necesita volver a agregar manualmente la sublista invertida, ya que la reversión se realiza en la lista original . Así que todo lo que necesitas es dejar caer

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

y deja solo este código:

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


List.subList devuelve una vista en vivo de la lista entre los elementos especificados, no una copia de esos elementos (ver documentation ), por lo tanto, agregar a la lista original también modificará la sublista, lo que conducirá a ConcurrentModificationException (ya que lo que se agrega y a lo que agrega también se modifican al mismo tiempo).

list.subList(startIndex, endIndex+1)

Puede arreglar un código copiando la lista, como

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


de la documentación de ArrayList.subList :

La lista devuelta está respaldada por esta lista, por lo que los cambios no estructurales en la lista devuelta se reflejan en esta lista, y viceversa.

Entonces, cuando intenta agregar elementos en el índice de la ''vista'' de la sublista, se crea una modificación concurrente.