Java 8 Streams: Convertir List en Map
Introducción
En esta publicación veremos como emplear los streams introducidos en Java 8 para obtener un Map
a partir de un List
.
Los mapas son estructuras de datos compuestas por una colección de elementos clave-valor de forma que una clave sólo puede existir de forma única dentro de la colección. Esto nos permite realizar búsquedas de elementos de forma muy rápida a partir de su clave sin necesidad de recorrer la colección completa.
Es muy común que al enfrentarnos con algún problema partamos de una lista de elementos y queramos obtener un mapa de forma que podamos optimizar el algoritmo para la resolución del mismo. Es en estas ocasiones donde los streams de Java 8 nos permiten hacer esta conversión de forma rápida y elegante.
En esta publicación veremos como convertir una lista de repositorios de Github en un mapa. Para ello partimos de la clase GithubRepo.
1/** ... **/
2public class GithubRepo {
3 private String name;
4 @JsonProperty("full_name")
5 private String fullName;
6 private String description;
7 private Boolean fork;
8 @JsonIgnore
9 private Integer localVersion;
10 /** ... **/
11}
Lista a Mapa sin duplicidades
Si estamos seguros de que la lista que queremos convertir a un Map
no contiene elementos duplicados, podemos emplear el siguiente código:
1repos.stream().collect(Collectors.toMap(GithubRepo::getName, Function.identity()));
El primer paso es convertir la lista en un Stream
y seguidamente, sin hacer ninguna transformación, recoger los resultados con uno de los Collector
s que ofrece Java 8 por defecto.
Como clave para el mapa vamos a emplear el nombre del repositorio en este caso utilizamos el código GithubRepo::getName
que sería el equivalente recomendado a la expresión gr -> gr.getName()
. Como valor del mapa utilizaremos el propio objeto de la lista, para ello java nos ofrece Function.identity()
que sería el equivalente recomendado a la expresión gr -> gr
.
Como hemos dicho, una de las premisas fundamentales de este Collector
es que no haya claves duplicadas dentro de la lista inicial. En el caso de recibir una duplicidad, se lanzaría un IllegalStateException
como puede comprobarse en el test listToMap_duplicatesList_shouldThrowException.
Lista a Mapa con claves duplicadas
En el caso en el que no estemos seguros o sepamos que la lista inicial va a contener duplicidades emplearemos otro Collector
que nos permite gestionar estas duplicidades. En este caso debemos pasar una función adicional que se encargará de fusionar los elementos duplicados.
1repos.stream().collect(Collectors.toMap(GithubRepo::getName, Function.identity(),
2 (ghrPrevious, ghrNew) -> ghrNew));
En la función toMap
, aportamos un tercer parámetro que a partir de 2 elementos iniciales ghrPrevious y ghrNew debe de retornar un único elemento del mismo tipo. En este caso estamos devolviendo el más nuevo de los dos, aunque podría devolverse el más antiguo o una transformación sobre alguno de ellos o sobre uno nuevo.
Lista a Mapa manteniendo orden de claves
Collectors.toMap
admite un parámetro adicional donde podemos especificar el constructor del Mapa que devolverá la función.
1repos.stream().collect(Collectors.toMap(GithubRepo::getName, Function.identity(),
2 (ghrPrevious, ghrNew) -> ghrNew, TreeMap::new));
En este caso indicamos que queremos obtener un Map
como instancia de TreeMap
que implementa la interfaz SortedMap
. Este tipo de mapa está ordenado por el orden natural de sus claves o por el orden especificado en el comparador de claves suministrado como argumento al constructor.
En el caso anterior, el mapa estará ordenado por el orden alfabético de las claves, en el siguiente código lo ordenaríamos por el orden inverso al emplear Comparator.reverseOrder()
:
1repos.stream().collect(Collectors.toMap(GithubRepo::getName, Function.identity(),
2 (ghrPrevious, ghrNew) -> ghrNew,
3 () -> new TreeMap(Comparator.reverseOrder())));
Conclusión
En esta publicación hemos visto como podemos pasar de forma sencilla una estructura de datos de tipo lista a un mapa empleando streams de Java 8 y la función Collectors.toMap
.
El código fuente completo de este artículo puede encontrarse en GitHub.