remove delete java loops iterator foreach

delete - remove if java



Llamando a remove in foreach loop en Java (11)

En Java, ¿es legal llamar a eliminar en una colección cuando se recorre la colección utilizando un bucle foreach? Por ejemplo:

List<String> names = .... for (String name : names) { // Do something names.remove(name). }

Como anexo, ¿es legal eliminar elementos que aún no se han repetido? Por ejemplo,

//Assume that the names list as duplicate entries List<String> names = .... for (String name : names) { // Do something while (names.remove(name)); }


  1. Intente esto 2. y cambie la condición a "INVIERNO" y se preguntará:

public static void main(String[] args) { Season.add("Frühling"); Season.add("Sommer"); Season.add("Herbst"); Season.add("WINTER"); for (String s : Season) { if(!s.equals("Sommer")) { System.out.println(s); continue; } Season.remove("Frühling"); } }


Aquellos que dicen que no puede eliminar de forma segura un elemento de una colección, excepto que a través del Iterador no son del todo correctos, puede hacerlo de forma segura utilizando una de las colecciones concurrentes, como ConcurrentHashMap.


Asegúrese de que este no es el olor del código. ¿Es posible revertir la lógica y ser "inclusivo" en lugar de "exclusivo"?

List<String> names = .... List<String> reducedNames = .... for (String name : names) { // Do something if (conditionToIncludeMet) reducedNames.add(name); } return reducedNames;

La situación que me llevó a esta página involucraba un código antiguo que recorría una Lista usando indecies para eliminar elementos de la Lista. Quería refactorizarlo para usar el estilo foreach.

Recorrió una lista completa de elementos para verificar a cuáles tenía permiso para acceder el usuario y eliminó los que no tenían permiso de la lista.

List<Service> services = ... for (int i=0; i<services.size(); i++) { if (!isServicePermitted(user, services.get(i))) services.remove(i); }

Para revertir esto y no usar el quitar:

List<Service> services = ... List<Service> permittedServices = ... for (Service service:services) { if (isServicePermitted(user, service)) permittedServices.add(service); } return permittedServices;

¿Cuándo se preferiría "eliminar"? Una consideración es si gien una lista grande o un "agregado" costoso, combinado con solo unos pocos eliminados en comparación con el tamaño de la lista. Podría ser más eficiente hacer solo unas cuantas eliminaciones en lugar de muchas adiciones. Pero en mi caso la situación no merecía tal optimización.


El diseño java del "bucle mejorado" fue para no exponer el iterador al código, pero la única forma de eliminar un elemento de manera segura es acceder al iterador. Así que en este caso tienes que hacerlo de la vieja escuela:

for(Iterator<String> i = names.iterator(); i.hasNext();) { String name = i.next(); //Do Something i.remove(); }

Si en el código real el bucle for mejorado realmente vale la pena, entonces podría agregar los elementos a una colección temporal y llamar a removeAll en la lista después del bucle.

EDITAR (volver a agregar): No, cambiar la lista de cualquier manera fuera del método iterator.remove () mientras se realiza la iteración causará problemas. La única forma de evitar esto es usar un CopyOnWriteArrayList, pero realmente está destinado a problemas de concurrencia.

La forma más barata (en términos de líneas de código) de eliminar duplicados es volcar la lista en un LinkedHashSet (y luego volver a la lista si es necesario). Esto conserva el orden de inserción al eliminar duplicados.


Es mejor usar un iterador cuando desea eliminar un elemento de una lista

porque el código fuente de eliminar es

if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null;

por lo tanto, si elimina un elemento de la lista, la lista se reestructurará, se cambiará el índice del otro elemento, esto puede resultar en algo que usted desea que suceda.


No quieres hacer eso. Puede causar un comportamiento indefinido dependiendo de la colección. Quieres usar un Iterator directamente. Aunque para cada construcción es azúcar sintáctica y realmente está usando un iterador, lo oculta de su código para que no pueda acceder a él para llamar a Iterator.remove .

El comportamiento de un iterador no se especifica si la colección subyacente se modifica mientras la iteración está en curso de otra manera que no sea llamando a este método.

En su lugar escribe tu código:

List<String> names = .... Iterator<String> it = names.iterator(); while (it.hasNext()) { String name = it.next(); // Do something it.remove(); }

Tenga en cuenta que el código llama a List.remove , no a List.remove .

Apéndice:

Incluso si está eliminando un elemento que aún no se ha iterado, todavía no desea modificar la colección y luego usar el Iterator . Podría modificar la colección de una manera que sea sorprendente y que afecte las operaciones futuras en el Iterator .


No sabía sobre los iteradores, sin embargo, esto es lo que estaba haciendo hasta hoy para eliminar elementos de una lista dentro de un bucle:

List<String> names = .... for (i=names.size()-1;i>=0;i--) { // Do something names.remove(i); }

Esto siempre funciona y podría usarse en otros idiomas o estructuras que no admitan iteradores.


Para eliminar de forma segura de una colección mientras se itera sobre ella, debe utilizar un iterador.

Por ejemplo:

List<String> names = .... Iterator<String> i = names.iterator(); while (i.hasNext()) { String s = i.next(); // must be called before you can call i.remove() // Do something i.remove(); }

De la documentación de Java :

Los iteradores devueltos por el iterador de esta clase y los métodos listIterator son a prueba de fallas: si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier manera, excepto a través de los propios métodos eliminar o agregar del iterador, el iterador emitirá una excepción ConcurrentModification. Por lo tanto, ante una modificación concurrente, el iterador falla de manera rápida y limpia, en lugar de arriesgarse a comportamientos arbitrarios y no deterministas en un momento indeterminado en el futuro.

Quizás lo que no está claro para muchos novatos es el hecho de que la iteración sobre una lista usando las construcciones for / foreach crea implícitamente un iterador que es necesariamente inaccesible. Esta información se puede encontrar here


Sí, puede usar el bucle for-each. Para hacerlo, debe mantener una lista separada para retener los elementos eliminados y luego eliminar esa lista de la lista de nombres utilizando el método removeAll() .

List<String> names = .... // introduce a separate list to hold removing items List<String> toRemove= new ArrayList<String>(); for (String name : names) { // Do something: perform conditional checks toRemove.add(name); } names.removeAll(toRemove); // now names list holds expected values


Utilizar

.remove () de Interator o

Utilizar

CopyOnWriteArrayList


for (String name : new ArrayList<String>(names)) { // Do something names.remove(nameToRemove); }

Clona los names la lista y repite la secuencia mientras elimina de la lista original. Un poco más limpio que la respuesta superior.