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 addAllmétodo al final.
Usando ListIterator
Si está trabajando con listas, otra técnica consiste en utilizar una ListIteratorque 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 addmé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 removeIfmétodo en la Collectionclase 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 removeAlllos 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.subSetmé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
removeAltécnica funcionan con cualquier Colección (Colección, Lista, Conjunto, etc.).
- La
ListIteratortécnica obviamente solo funciona con listas, siempre que su ListIteratorimplementación dada ofrezca soporte para operaciones de agregar y quitar.
- El
Iteratorenfoque funcionaría con cualquier tipo de colección, pero solo admite operaciones de eliminación.
- Con el enfoque
ListIterator/ Iteratorla 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
removeAllenfoque, 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
Iteratorinterfaz está marcado como "opcional" en Javadocs, lo que significa que podría haber Iteratorimplementaciones que arrojen UnsupportedOperationExceptionsi 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.