JavaRush /Blog Java /Random-ES /Eliminar un elemento de un ArrayList en Java

Eliminar un elemento de un ArrayList en Java

Publicado en el grupo Random-ES
¡Hola! En la última conferencia, nos familiarizamos con la clase ArrayList y también aprendimos cómo realizar las operaciones más comunes con ella. Además, hemos resaltado bastantes diferencias entre ArrayList y una matriz normal. Ahora veamos cómo eliminar un elemento de ArrayList. Ya hemos dicho que eliminar elementos en una matriz normal no es muy conveniente. Eliminar un elemento de un ArrayList - 1Como no podemos eliminar la celda en sí, sólo podemos poner a cero 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("Tomás");
       cats[1] = new Cat("Hipopótamo");
       cats[2] = new Cat("Felipe Markovich");

       cats[1] = null;

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


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Conclusión:

[Cat{name='Томас'}, null, Cat{name='Фoпп Маркович'}]
Pero cuando se reinicia, queda un "agujero" en la matriz. No eliminamos la celda, sino solo su contenido. Imagínese lo que sucederá si tenemos un conjunto de 50 gatos, 17 de los cuales eliminamos de esta manera. ¡Tendremos un campo de 17 hoyos y los cuidaremos! Recordar de memoria el número de celdas vacías donde se pueden escribir nuevos valores no es realista. Si comete un error una vez, sobrescribirá la celda con la referencia deseada al objeto. Por supuesto, existe la posibilidad de hacerlo con un poco más de cuidado: después de eliminar, mueva los elementos de la matriz al principio, de modo que el "agujero" quede al final:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Tomás");
   cats[1] = new Cat("Hipopótamo");
   cats[2] = new Cat("Felipe Markovich");
   cats[3] = new Cat("Pelusa");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //mover los elementos al principio para que la celda vacía quede al final
       cats[i-1] = cats[i];
       cats[i] = null;
   }

   System.out.println(Arrays.toString(cats));
}
Conclusión:

[Cat{name='Томас'}, Cat{name='Фoпп Маркович'}, Cat{name='Пушок'}, null]
Ahora parece tener mejor aspecto, pero difícilmente se puede considerar una solución estable. ¡Como mínimo, porque tendremos que escribir este código a mano cada vez que eliminemos un elemento del array! Mala opción. Podrías ir por el otro lado y crear un método separado:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...eliminamos el gato por índice y desplazamos los elementos
}
Pero esto también es de poca utilidad: este método solo puede funcionar con objetos Cat, pero no puede funcionar con otros. Es decir, si en el programa hay 100 clases más con las que queremos usar arrays, tendremos que escribir el mismo método con exactamente la misma lógica en cada una de ellas. Esto es un completo fracaso -_- ¡Pero en la clase ArrayList este problema se resuelve con éxito! Implementa un método especial para eliminar elementos remove():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomás");
   Cat behemoth = new Cat("Hipopótamo");
   Cat philipp = new Cat("Felipe Markovich");
   Cat pushok = new Cat("Pelusa");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Pasamos el índice de nuestro objeto al método y se eliminó (como en una matriz). El método remove()tiene dos características. En primer lugar , no deja “agujeros”. Ya implementa la lógica de cambiar elementos al quitar un elemento del medio, que anteriormente escribimos a mano. Mire el resultado del código anterior en la consola:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фoпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фoпп Маркович'}, Cat{name='Пушок'}]
Quitamos un gato del medio y los demás los movimos para que no quedaran espacios. En segundo lugar , puede eliminar un objeto no sólo por índice (como una matriz normal), sino también por referencia al objeto :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomás");
   Cat behemoth = new Cat("Hipopótamo");
   Cat philipp = new Cat("Felipe Markovich");
   Cat pushok = new Cat("Pelusa");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
Conclusión:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фoпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Esto puede resultar muy útil si no desea tener siempre en la cabeza el índice del objeto deseado. Parece que hemos solucionado la eliminación habitual. Ahora imaginemos esta situación: queremos recorrer nuestra lista de elementos y eliminar un gato con un nombre determinado. Para ello utilizamos un operador de bucle especial for: for each. Puedes aprender más sobre esto en esta conferencia .
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomás");
   Cat behemoth = new Cat("Hipopótamo");
   Cat philipp = new Cat("Felipe Markovich");
   Cat pushok = new Cat("Pelusa");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

       if (cat.name.equals("Hipopótamo")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
El código parece bastante lógico. Sin embargo, el resultado puede sorprenderte:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Algún tipo de error y no está claro por qué apareció de repente. Hay una serie de matices en este proceso que es necesario abordar. Una regla general que debes recordar: no puedes iterar a través de una colección y cambiar sus elementos al mismo tiempo. Sí, sí, exactamente un cambio, y no sólo una eliminación. Si intentas en nuestro código reemplazar la eliminación de gatos con la inserción de otros nuevos, el resultado será el mismo:
for (Cat cat: cats) {

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

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Cambiamos una operación por otra, pero el resultado no cambió: el mismo error ConcurrentModificationException. Ocurre precisamente cuando intentamos romper una regla y cambiar la lista mientras la iteramos. En Java, para eliminar elementos durante la iteración, es necesario utilizar un objeto especial: un iterador (clase Iterator). La clase Iteratores responsable de recorrer de forma segura una lista de elementos. Es bastante sencillo porque tiene sólo 3 métodos:
  • hasNext()- regresa truedependiendo falsede 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á literalmente "adaptado" a nuestras necesidades y no tiene nada de complicado. Por ejemplo, queremos comprobar si nuestra lista contiene el siguiente elemento y, de ser así, imprimirlo en la consola:
Iterator<Cat> catIterator = cats.iterator();//crear un iterador
while(catIterator.hasNext()) {//siempre y cuando haya elementos en la lista

   Cat nextCat = catIterator.next();// obtener el siguiente elemento
   System.out.println(nextCat);// imprimirlo en la consola
}
Conclusión:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фoпп Маркович'}
Cat{name='Пушок'}
Como puede ver, la clase ArrayListya implementa un método especial para crear un iterador: iterator(). Además, tenga en cuenta que al crear un iterador, especificamos la clase de objetos con los que funcionará ( <Cat>). En última instancia, podemos resolver fácilmente nuestro problema original utilizando un iterador. Por ejemplo, eliminemos un gato llamado "Philip Markovich":
Iterator<Cat> catIterator = cats.iterator();//crear un iterador
while(catIterator.hasNext()) {//siempre y cuando haya elementos en la lista

   Cat nextCat = catIterator.next();// obtener el siguiente elemento
   if (nextCat.name.equals("Felipe Markovich")) {
       catIterator.remove();//eliminar el gato con el nombre deseado
   }
}

System.out.println(cats);
Conclusión:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
¡Es posible que hayas notado que no especificamos ni el índice del elemento ni el nombre de la variable de referencia en el método iterador remove()! El iterador es más inteligente de lo que parece: el método remove()elimina el último elemento devuelto por el iterador. Como puedes ver, funcionó exactamente como era necesario :) Esto es básicamente todo lo que necesitas saber sobre cómo eliminar elementos de ArrayList. Más precisamente, casi todo. En la próxima conferencia veremos el “interior” de esta clase y veremos qué sucede allí durante las operaciones :) ¡Nos vemos!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION