Permítanme dar algunos ejemplos con algunas alternativas para evitar a ConcurrentModificationException
.
Supongamos que tenemos la siguiente colección de libros.
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Recoger y quitar
La primera técnica consiste en recopilar todos los objetos que queremos eliminar (por ejemplo, utilizando un bucle for mejorado) y después de terminar de iterar, eliminamos todos los objetos encontrados.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
Esto supone que la operación que desea hacer es "eliminar".
Si desea "agregar" este enfoque también funcionaría, pero supongo que iteraría sobre una colección diferente para determinar qué elementos desea agregar a una segunda colección y luego emitir un addAll
método al final.
Usando ListIterator
Si está trabajando con listas, otra técnica consiste en utilizar una ListIterator
que tenga soporte para eliminar y agregar elementos durante la iteración misma.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Nuevamente, usé el método "eliminar" en el ejemplo anterior, que es lo que su pregunta parecía implicar, pero también puede usar su add
método para agregar nuevos elementos durante la iteración.
Usando JDK> = 8
Para aquellos que trabajan con Java 8 o versiones superiores, hay un par de otras técnicas que podría utilizar para aprovecharlo.
Podría usar el nuevo removeIf
método en la Collection
clase base:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
O use la nueva API de transmisión:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
En este último caso, para filtrar elementos de una colección, reasigne la referencia original a la colección filtrada (es decir books = filtered
) o utilice la colección filtrada a removeAll
los elementos encontrados de la colección original (es decir books.removeAll(filtered)
).
Usar sublista o subconjunto
También hay otras alternativas. Si la lista está ordenada y desea eliminar elementos consecutivos, puede crear una sublista y luego borrarla:
books.subList(0,5).clear();
Como la sublista está respaldada por la lista original, esta sería una forma eficiente de eliminar esta subcolección de elementos.
Algo similar podría lograrse con conjuntos ordenados utilizando el NavigableSet.subSet
método, o cualquiera de los métodos de corte que se ofrecen allí.
Consideraciones:
El método que utilice puede depender de lo que tenga intención de hacer.
- La colección y la
removeAl
técnica funcionan con cualquier Colección (Colección, Lista, Conjunto, etc.).
- La
ListIterator
técnica obviamente solo funciona con listas, siempre que su ListIterator
implementación dada ofrezca soporte para operaciones de agregar y quitar.
- El
Iterator
enfoque funcionaría con cualquier tipo de colección, pero solo admite operaciones de eliminación.
- Con el enfoque
ListIterator
/ Iterator
la ventaja obvia es no tener que copiar nada, ya que eliminamos a medida que iteramos. Entonces, esto es muy eficiente.
- El ejemplo de secuencias JDK 8 en realidad no eliminó nada, sino que buscó los elementos deseados, y luego reemplazamos la referencia de colección original con la nueva, y dejamos que se recolectara la basura. Entonces, iteramos solo una vez sobre la colección y eso sería eficiente.
- En la recopilación y el
removeAll
enfoque, la desventaja es que tenemos que repetir dos veces. Primero iteramos en el bucle de búsqueda buscando un objeto que coincida con nuestros criterios de eliminación, y una vez que lo hemos encontrado, pedimos eliminarlo de la colección original, lo que implicaría un segundo trabajo de iteración para buscar este artículo para eliminarlo
- Creo que vale la pena mencionar que el método remove de la
Iterator
interfaz está marcado como "opcional" en Javadocs, lo que significa que podría haber Iterator
implementaciones que arrojen UnsupportedOperationException
si invocamos el método remove. Como tal, diría que este enfoque es menos seguro que otros si no podemos garantizar el soporte del iterador para la eliminación de elementos.