Eliminar elementos de las colecciones de Java

1. Información general

En este tutorial rápido, hablaremos sobre cuatro formas diferentes de eliminar elementos de las colecciones de Java que coinciden con ciertos predicados.

Naturalmente, también veremos algunas de las advertencias.

2. Definición de nuestra colección

Primero, vamos a ilustrar dos enfoques que mutan la estructura de datos original. Luego hablaremos de otras dos opciones que en lugar de eliminar los elementos, crearán una copia de la Colección original sin ellos.

Usemos la siguiente colección a lo largo de nuestros ejemplos para demostrar cómo podemos lograr el mismo resultado usando diferentes métodos:

Collection names = new ArrayList(); names.add("John"); names.add("Ana"); names.add("Mary"); names.add("Anthony"); names.add("Mark");

3. Eliminación de elementos con iterador

El Iterador de Java nos permite caminar y eliminar cada elemento individual dentro de una Colección .

Para hacerlo, primero necesitamos recuperar un iterador sobre sus elementos usando el método de iterador . Luego, podemos visitar cada elemento con la ayuda de next y eliminarlos usando remove :

Iterator i = names.iterator(); while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); } }

A pesar de su simplicidad, hay algunas advertencias que debemos considerar:

  • Dependiendo de la colección, podemos encontrarnos con las excepciones de ConcurrentModificationException
  • Necesitamos iterar sobre los elementos antes de poder eliminarlos.
  • Dependiendo de la colección, remove puede comportarse de manera diferente a lo esperado. Por ejemplo: ArrayList.Iterator elimina el elemento de la colección y desplaza los datos posteriores a la izquierda, mientras que LinkedList.Iterator simplemente ajusta el puntero al siguiente elemento. Como tal, LinkedList.Iterator funciona mucho mejor que ArrayList.Iterator al eliminar elementos

4. Java 8 y Collection.removeIf ()

Java 8 introdujo un nuevo método en la interfaz Collection que proporciona una forma más concisa de eliminar elementos usando Predicate :

names.removeIf(e -> e.startsWith("A"));

Es importante señalar que, contrariamente a la iterador enfoque, removeIf realiza igualmente bien tanto en LinkedList y ArrayList .

En Java 8, ArrayList anula la implementación predeterminada, que se basa en Iterator , e implementa una estrategia diferente: primero, itera sobre los elementos y marca los que coinciden con nuestro Predicado; luego, itera por segunda vez para eliminar (y desplazar) los elementos que fueron marcados en la primera iteración.

5. Java 8 y la introducción de Stream

Una de las nuevas características principales de Java 8 fue la incorporación de Stream (y Collectors ). Hay muchas formas de crear una secuencia a partir de una fuente. Sin embargo, la mayoría de las operaciones que afectan a la instancia de Stream no mutarán su fuente, sino que la API se enfoca en crear copias de una fuente y realizar cualquier operación que podamos necesitar en ellas.

Echemos un vistazo a cómo podemos usar Stream y Collectors para buscar / filtrar elementos que coincidan y no coincidan con nuestro Predicado .

5.1. Eliminar elementos con Stream

Eliminar, o mejor dicho, filtrar elementos usando Stream es bastante sencillo , solo necesitamos crear una instancia de Stream usando nuestra Colección , invocar el filtro con nuestro Predicado y luego recopilar el resultado con la ayuda de Collectors:

Collection filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());

La transmisión es menos invasiva que los enfoques anteriores, promueve el aislamiento y permite la creación de múltiples copias de la misma fuente. Sin embargo, debemos tener en cuenta que también aumenta la memoria utilizada por nuestra aplicación.

5.2. Collectors.partitioningBy

La combinación de Stream.filter y Collectors es bastante útil, aunque es posible que nos encontremos con escenarios en los que necesitemos elementos coincidentes y no coincidentes. En tales casos, podemos aprovechar Collectors.partitioningBy :

Map
    
      classifiedElements = names .stream() .collect(Collectors.partitioningBy((String e) -> !e.startsWith("A"))); String matching = String.join(",", classifiedElements.get(true)); String nonMatching = String.join(",", classifiedElements.get(false));
    

Este método devuelve un mapa que solo contiene dos claves, verdadero y falso , cada una de las cuales apunta a una lista que contiene los elementos coincidentes y no coincidentes, respectivamente.

6. Conclusión

En este artículo, analizamos algunos métodos para eliminar elementos de las colecciones y algunas de sus advertencias.

Puede encontrar el código fuente completo y todos los fragmentos de código de este artículo en GitHub.