1. Información general
En este breve tutorial, veremos algunas formas diferentes de contar los elementos duplicados en una ArrayList .
2. Bucle con Map.put ()
Nuestro resultado esperado sería un objeto Map , que contiene todos los elementos de la lista de entrada como claves y el recuento de cada elemento como valor.
La solución más sencilla para lograr esto sería recorrer la lista de entrada y para cada elemento:
- si el resultMap contiene el elemento, incrementamos un contador en 1
- de lo contrario, nos ponemos una nueva entrada de mapa (elemento, 1) al mapa
public Map countByClassicalLoop(List inputList) { Map resultMap = new HashMap(); for (T element : inputList) { if (resultMap.containsKey(element)) { resultMap.put(element, resultMap.get(element) + 1L); } else { resultMap.put(element, 1L); } } return resultMap; }
Esta implementación tiene la mejor compatibilidad, ya que funciona para todas las versiones modernas de Java.
Si no necesitamos la compatibilidad anterior a Java 8, podemos simplificar aún más nuestro método:
public Map countByForEachLoopWithGetOrDefault(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.put(e, resultMap.getOrDefault(e, 0L) + 1L)); return resultMap; }
A continuación, creemos una lista de entrada para probar el método:
private List INPUT_LIST = Lists.list( "expect1", "expect2", "expect2", "expect3", "expect3", "expect3", "expect4", "expect4", "expect4", "expect4");
Y ahora verifiquémoslo:
private void verifyResult(Map resultMap) { assertThat(resultMap) .isNotEmpty().hasSize(4) .containsExactly( entry("expect1", 1L), entry("expect2", 2L), entry("expect3", 3L), entry("expect4", 4L)); }
Reutilizaremos este arnés de prueba para el resto de nuestros enfoques.
3. Haz un bucle con Map.compute ()
En Java 8, el práctico método compute () se ha introducido en la interfaz de Map . También podemos hacer uso de este método:
public Map countByForEachLoopWithMapCompute(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.compute(e, (k, v) -> v == null ? 1L : v + 1L)); return resultMap; }
Note (k, v) -> v == null? 1L: v + 1L es la función de reasignación que implementa la interfaz BiFunction . Para una clave determinada, devuelve su valor actual incrementado en uno (si la clave ya está presente en el mapa) o devuelve el valor predeterminado de uno.
Para hacer que el código sea más legible, podríamos extraer la función de reasignación a su variable o incluso tomarla como parámetro de entrada para countByForEachLoopWithMapCompute.
4. Bucle con Map.merge ()
Cuando usamos Map.compute () , debemos manejar los valores nulos explícitamente, por ejemplo, si no existe un mapeo para una clave dada. Es por eso que hemos implementado una verificación nula en nuestra función de reasignación. Sin embargo, esto no se ve bonito.
Limpiemos más nuestro código con la ayuda del método Map.merge () :
public Map countByForEachLoopWithMapMerge(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.merge(e, 1L, Long::sum)); return resultMap; }
Ahora el código se ve limpio y conciso.
Expliquemos cómo funciona merge () . Si el mapeo para una clave dada no existe, o su valor es nulo , asocia la clave con el valor proporcionado. De lo contrario, calcula un nuevo valor utilizando la función de reasignación y actualiza la asignación en consecuencia.
Observe que esta vez usamos Long :: sum como implementación de la interfaz BiFunction .
5. Transmitir API Collectors.toMap ()
Como ya hemos hablado de Java 8, no podemos olvidarnos de la potente API Stream. Gracias a la API Stream, podemos solucionar el problema de una forma muy compacta.
El colector toMap () nos ayuda a convertir la lista de entrada en un mapa :
public Map countByStreamToMap(List inputList) { return inputList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum)); }
El tomap () es un colector conveniente, que nos puede ayudar a transformar la corriente en diferentes Mapa implementaciones.
6. Transmitir API Collectors.groupingBy () y Collectors.counting ()
Excepto por el toMap () , nuestro problema puede ser resuelto por otros dos colectores, groupingBy () y counting () :
public Map countByStreamGroupBy(List inputList) { return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting())); }
El uso adecuado de los recopiladores de Java 8 hace que nuestro código sea compacto y fácil de leer.
7. Conclusión
En este artículo rápido, ilustramos varias formas de calcular el recuento de elementos duplicados en una lista.
Si desea repasar el ArrayList en sí, puede consultar el artículo de referencia.
Como siempre, el código fuente completo está disponible en GitHub.