CodeGym /Blog Java /Random-ES /Eliminar un elemento de un ArrayList
Autor
Volodymyr Portianko
Java Engineer at Playtika

Eliminar un elemento de un ArrayList

Publicado en el grupo Random-ES
¡Hola! En la última lección, nos familiarizamos con la ArrayListclase y aprendimos cómo realizar las operaciones más comunes con esta clase. Además, señalamos varias diferencias entre una ArrayListy una matriz ordinaria. Pero eludimos un tema, a saber, cómo eliminar elementos de un archivo ArrayList. Discutiremos eso ahora. Eliminar un elemento de un ArrayList - 1Ya hemos mencionado que eliminar elementos de una matriz ordinaria no es muy conveniente. Dado que no podemos eliminar el elemento en sí, solo podemos "poner a cero" (establecer en nulo) su valor:

public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }

   
@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Salida: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Pero establecer un elemento de matriz en nulo deja un "agujero". No hemos eliminado la posición en la matriz, solo su contenido. Imagina lo que sucedería si tuviéramos una matriz de 50 gatos y elimináramos 17 de esta manera. Tendremos una matriz con 17 hoyos. ¡Intenta hacer un seguimiento de ellos! No es realista esperar recordar la cantidad de celdas vacías donde puede escribir nuevos valores. Si comete un error, sobrescribirá una referencia de objeto que desee. Hay, por supuesto, una manera de hacer esto con un poco más de cuidado: después de eliminar un elemento, mueva los elementos al frente de la matriz para colocar el "agujero" al final:

public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Behemoth");
   cats[2] = new Cat("Lionel Messi");
   cats[2] = new Cat("Fluffy");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
   }

   System.out.println(Arrays.toString(cats));
}
Salida: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Esto parece mejor, pero difícilmente puede llamarse una solución robusta. ¡Si no fuera por otra razón que el hecho de que tenemos que escribir este código cada vez que eliminamos un elemento de una matriz! Esta es una mala opción. Podríamos ir de otra manera y crear un método separado:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Pero esto también sirve de poco: este método solo puede funcionar con Catobjetos, pero no con otros tipos. En otras palabras, si un programa tiene otras 100 clases que queremos usar con arreglos, tendremos que escribir el mismo método con exactamente la misma lógica en cada una de ellas. Esto es un desastre total -_- ¡Pero la ArrayListclase resuelve este problema! Implementa un método especial para eliminar elementos:remove()

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Pasamos el índice de nuestro objeto al método, que lo elimina (como en una matriz). El remove()método tiene dos características especiales. En primer lugar, no deja "agujeros". Ya implementa la lógica necesaria para cambiar elementos cuando se elimina un elemento del medio, que previamente escribimos nosotros mismos. Mira el resultado del código anterior:

[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Quitamos un gato del medio y el resto se movió para que no hubiera espacios vacíos. En segundo lugar , puede eliminar objetos no solo por índice (como una matriz normal), sino también por referencia :

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(lionel);

   System.out.println(cats.toString());
}
Salida: [Gato{nombre='Thomas'}, Gato{nombre='Behemoth'}, Gato{nombre='Lionel Messi'}, Gato{nombre='Fluffy'}] [Gato{nombre='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Esto puede ser muy conveniente si no desea realizar un seguimiento constante del índice del objeto deseado. Parece que hemos descubierto la eliminación ordinaria. Ahora imaginemos esta situación: queremos iterar sobre nuestra lista y eliminar un gato con un nombre específico . Para hacer esto, usaremos un forbucle rápido (también llamado bucle for-each), que nos presentaron en las lecciones de Rishi:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   for (Cat cat: cats) {

       if (cat.name.equals("Behemoth")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
El código parece perfectamente lógico. Pero el resultado podría ser una gran sorpresa: excepción en el subproceso "principal" java.util.ConcurrentModificationException en java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) en java.util.ArrayList$Itr.next(ArrayList. java:831) en Cat.main(Cat.java:25) Hay algún tipo de error y no está claro por qué ocurrió. Este proceso implica una serie de matices que es necesario abordar. Esta es la regla general que debe recordar: no puede iterar sobre una colección y cambiar sus elementos simultáneamente. Y nos referimos a cualquier tipo de cambio, no simplemente a la eliminación. Si reemplaza la eliminación del gato con un intento de insertar nuevos gatos, el resultado será el mismo:

for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhagen"));
}

System.out.println(cats);
Excepción en el subproceso "principal" java.util.ConcurrentModificationException en java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) en java.util.ArrayList$Itr.next(ArrayList.java:831) en Cat.main( Cat.java:25) Cambiamos una operación por otra, pero el resultado no cambió: obtenemos la misma ConcurrentModificationException . Ocurre precisamente cuando intentamos romper la regla anterior al cambiar la lista mientras se itera sobre ella. En Java, necesitamos un objeto especial llamado iterador (Iteratorclase) para eliminar elementos mientras itera sobre una colección. LaIteratorclase es responsable de iterar de forma segura sobre la lista de elementos. Es bastante sencillo, ya que solo tiene 3 métodos:
  • hasNext()- devuelve verdadero o falso, dependiendo de si hay un siguiente elemento en la lista o si ya hemos llegado al último.
  • next()- devuelve el siguiente elemento de la lista
  • remove()- elimina un elemento de la lista
Como puede ver, el iterador está hecho a la medida de nuestras necesidades y, al mismo tiempo, no tiene nada de complicado. Supongamos que queremos verificar si hay un elemento siguiente en nuestra lista y mostrarlo si lo hay:

Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list
  
   Cat nextCat = catIterator.next();// Get the next element
   System.out.println(nextCat);// Display it
}
Salida: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Como puede ver, ya ha implementado un método ArrayListespecial para crear un iterador: iterator(). Además, tenga en cuenta que cuando creamos un iterador, especificamos la clase de objetos con los que trabajará ( <Cat>). La conclusión es que un iterador maneja fácilmente nuestra tarea original. Por ejemplo, elimine el gato llamado "Lionel Messi":

Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   if (nextCat.name.equals("Lionel Messi")) {
       catIterator.remove();// Delete the cat with the specified name
   }
}

System.out.println(cats);
Salida: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Es posible que haya notado que no especificamos ni el índice ni el nombre en el remove()método del iterador ! El iterador es más inteligente de lo que parece: remove()elimina el último elemento devuelto por el iterador. Como puede ver, hizo exactamente lo que queríamos que hiciera :) En principio, esto es todo lo que necesita saber sobre cómo eliminar elementos de un archivo ArrayList. Bueno, casi todo. En la próxima lección, veremos dentro de esta clase y veremos qué sucede allí durante varias llamadas a métodos :) ¡Hasta entonces!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION