Eliminar datos de ArrayList con un bucle For

Tengo un problema extraño. Pensé que esto me costaría unos minutos, pero ahora estoy luchando por unas horas … Esto es lo que obtuve:

for (int i = 0; i < size; i++){ if (data.get(i).getCaption().contains("_Hardi")){ data.remove(i); } } 

La data es ArrayList. En ArrayList obtuve algunas cadenas (un total de 14 o más), y 9 de ellas obtuve el nombre _Hardi en él.

Y con el código anterior, quiero eliminarlos. Si replace data.remove(i); con un System.out.println luego imprime algo 9 veces, lo que es bueno, porque _Hardi está en el ArrayList 9 veces.

Pero cuando uso data.remove(i); entonces no elimina los 9, sino solo unos pocos. Hice algunas pruebas y también vi esto:

Cuando cambio el nombre de las cuerdas a: Hardi1 Hardi2 Hardi3 Hardi4 Hardi5 Hardi6

Luego elimina solo los números pares (1, 3, 5, etc.). Está omitiendo 1 todo el tiempo, pero no puede entender por qué.

¿Cómo arreglar esto? ¿O tal vez otra forma de eliminarlos?

El problema aquí es que está iterando de 0 a tamaño y dentro del ciclo está eliminando elementos . La eliminación de los elementos reducirá el tamaño de la lista, que fallará cuando intente acceder a los índices que son mayores que el tamaño efectivo (el tamaño después de los elementos eliminados).

Hay dos enfoques para hacer esto.

Eliminar utilizando iterador si no desea tratar con el índice.

 for (Iterator it = data.iterator(); it.hasNext();) { if (it.next().getCaption().contains("_Hardi")) { it.remove(); } } 

De lo contrario, eliminar desde el final.

 for (int i = size-1; i >= 0; i--){ if (data.get(i).getCaption().contains("_Hardi")){ data.remove(i); } } 

No debe eliminar elementos de una lista mientras itera sobre ella. En su lugar, use Iterator.remove() como:

 for (Iterator it = list.iterator(); it.hasNext();) { if ( condition is true ) { it.remove(); } } 

Cada vez que elimina un artículo, está cambiando el índice del que está delante (de modo que cuando elimina la lista [1], la lista [2] se convierte en la lista [1], de ahí el salto.

Aquí hay una forma muy fácil de hacerlo: (cuenta atrás en vez de subir)

for(int i = list.size() - 1; i>=0; i--) { if(condition...) list.remove(i); }
for(int i = list.size() - 1; i>=0; i--) { if(condition...) list.remove(i); } 

Tiene mucho sentido si lo piensas bien. Digamos que tiene una lista [A, B, C] . El primer pase a través del ciclo, i == 0 . Usted ve el elemento A y luego lo elimina, entonces la lista ahora es [B, C] , con el elemento 0 siendo B Ahora incrementas i al final del ciclo, entonces estás viendo la list[1] que es C

Una solución es reducir i cada vez que elimine un artículo, de modo que “cancele” el incremento posterior. Una mejor solución, como mat b arriba, es usar un Iterator que tiene una función integrada de remove() .

Hablando en general, es una buena idea, cuando enfrentas un problema como este, sacar una hoja de papel y simular que eres la computadora: revisa cada paso del ciclo y anota todas las variables a medida que avanzas. Eso habría hecho que el “salto” sea claro.

 for (Iterator it = data.iterator(); it.hasNext();) { if ( it.getCaption().contains("_Hardi")) { it.remove(); // performance is low O(n) } } 

Si su operación de eliminación se requiere mucho en la lista. Es mejor que uses LinkedList que da un mejor rendimiento Big O(1) (aproximadamente).

Donde en ArrayList el rendimiento es O(n) (aproximadamente). Por lo tanto, el impacto es muy alto en la operación de eliminación.

Es porque cuando elimina un elemento de una lista, los elementos de la lista se mueven hacia arriba. Por lo tanto, si elimina el primer elemento, es decir, en el índice 0, el elemento en el índice 1 se desplazará al índice 0, pero el contador de ciclos seguirá aumentando en cada iteración. así que, en lugar de obtener el elemento de índice 0º actualizado, obtiene el primer elemento de índice. Así que simplemente disminuya el contador por uno cada vez que elimine un elemento de su lista.

Puede usar el siguiente código para que funcione bien:

 for (int i = 0; i < data.size(); i++){ if (data.get(i).getCaption().contains("_Hardi")){ data.remove(i); i--; } } 

No entiendo por qué esta solución es la mejor para la mayoría de las personas.

 for (Iterator it = data.iterator(); it.hasNext();) { if (it.next().getCaption().contains("_Hardi")) { it.remove(); } } 

El tercer argumento está vacío, porque se movió a la siguiente línea. Además it.next() no solo incrementa la variable de loop sino que también la usa para obtener datos. Para mí, usar for loop es engañoso. ¿Por qué no usas while ?

 Iterator it = data.iterator(); while (it.hasNext()) { Object obj = it.next(); if (obj.getCaption().contains("_Hardi")) { it.remove(); } } 

Es tarde, pero podría funcionar para alguien.

 Iterator itr = yourList.iterator(); // remove the objects from list while (itr.hasNext()) { YourObject object = itr.next(); if (Your Statement) // id == 0 { itr.remove(); } } 

Esto sucede porque al eliminar los elementos, modifica el índice de una ArrayList .

Porque su índice ya no es bueno una vez que elimina un valor

Además, no podrá ajustar el size ya que si elimina un elemento, cambiará el tamaño.

Puede usar un iterator para lograr eso.

 import java.util.ArrayList; public class IteratorSample { public static void main(String[] args) { // TODO Auto-generated method stub ArrayList al = new ArrayList(); al.add(1); al.add(2); al.add(3); al.add(4); System.out.println("before removal!!"); displayList(al); for(int i = al.size()-1; i >= 0; i--){ if(al.get(i)==4){ al.remove(i); } } System.out.println("after removal!!"); displayList(al); } private static void displayList(ArrayList al) { for(int a:al){ System.out.println(a); } } } 

salida:

antes de la eliminación !! 1 2 3 4

¡Después de la eliminación! 1 2 3

Hay una manera más fácil de resolver este problema sin crear un nuevo objeto iterador. Aquí está el concepto. Supongamos que su arrayList contiene una lista de nombres:

 names = [James, Marshall, Susie, Audrey, Matt, Carl]; 

Para eliminar todo de Susie hacia adelante, simplemente obtenga el índice de Susie y asígnelo a una nueva variable:

 int location = names.indexOf(Susie);//index equals 2 

Ahora que tiene el índice, dígale a java que cuente la cantidad de veces que desea eliminar los valores de la lista de arreglos:

 for (int i = 0; i < 3; i++) { //remove Susie through Carl names.remove(names.get(location));//remove the value at index 2 } 

Cada vez que se ejecuta el valor del ciclo, la lista de conjunto se reduce en longitud. Dado que ha establecido un valor de índice y está contando el número de veces para eliminar valores, ya está todo listo. Aquí hay un ejemplo de salida después de cada paso:

  [2] names = [James, Marshall, Susie, Audrey, Matt, Carl];//first pass to get index and i = 0 [2] names = [James, Marshall, Audrey, Matt, Carl];//after first pass arrayList decreased and Audrey is now at index 2 and i = 1 [2] names = [James, Marshall, Matt, Carl];//Matt is now at index 2 and i = 2 [2] names = [James, Marshall, Carl];//Carl is now at index 3 and i = 3 names = [James, Marshall,]; //for loop ends 

Aquí hay un fragmento de cómo se verá tu método final:

 public void remove_user(String name) { int location = names.indexOf(name); //assign the int value of name to location if (names.remove(name)==true) { for (int i = 0; i < 7; i++) { names.remove(names.get(location)); }//end if print(name + " is no longer in the Group."); }//end method 

Este es un problema común durante el uso de Listas de Array y ocurre debido al hecho de que la longitud (tamaño) de un Arraylist puede cambiar. Mientras borras, el tamaño también cambia; entonces después de la primera iteración, tu código se vuelve loco. El mejor consejo es usar Iterator o loop desde atrás, aunque recomendaré el ciclo de backword porque creo que es menos complejo y funciona bien con numerosos elementos:

 //Let's decrement! for(int i = size-1; i >= 0; i--){ if (data.get(i).getCaption().contains("_Hardi")){ data.remove(i); } } 

¡Todavía tu código anterior, solo enlazó de manera diferente!

Espero que esto ayude…

¡Feliz encoding!

Además de las respuestas existentes, también puede usar un ciclo while regular con un incremento condicional:

 int i = 0; while (i < data.size()) { if (data.get(i).getCaption().contains("_Hardi")) data.remove(i); else i++; } 

Tenga en cuenta que se debe llamar a data.size() cada vez que data.size() el ciclo, de lo contrario terminará con una IndexOutOfBoundsException , ya que cada elemento eliminado altera el tamaño original de la lista.