Java Streams: Convertir List en Map
Introducción
Convertir un List en un Map es una de las operaciones más comunes cuando trabajamos con Java Streams. Los mapas son estructuras de datos compuestas por pares clave-valor donde cada clave es única, lo que permite realizar búsquedas rápidas sin necesidad de recorrer toda la colección.
En este artículo, te muestro todo lo que necesitas saber para convertir un List a un Map usando Collectors.toMap():
- Conversión básica cuando las claves son únicas
- Manejo de claves duplicadas con funciones de fusión
- Mantener el orden de inserción con LinkedHashMap
- Ordenar claves con TreeMap
- Crear mapas inmutables con toUnmodifiableMap()
- Manejo de valores nulos y cómo evitar NullPointerException
Para los ejemplos, usaré un record Repository:
public record Repository(String name, String fullName, String description, boolean fork) {}Consejo
Si usas una versión anterior a Java 16, puedes emplear una clase tradicional con getters.
Lista a Mapa sin duplicidades
Cuando estés seguro de que el List no contiene claves duplicadas, usa la versión básica de dos argumentos de Collectors.toMap().
El siguiente fragmento muestra la conversión básica:
repos.stream().collect(Collectors.toMap(Repository::name, Function.identity()));El primer paso es convertir la lista en un Stream y después recoger los resultados usando Collectors.toMap().
Para la clave del Map, usamos Repository::name (una referencia a método equivalente a r -> r.name()). Para el valor del Map, usamos Function.identity() que devuelve el mismo elemento (equivalente a r -> r).
Importante
Si la lista contiene claves duplicadas, lanzará un IllegalStateException. Consulta la siguiente sección para manejar duplicados.
Lista a Mapa con claves duplicadas
Cuando la lista pueda contener claves duplicadas, usa la versión de tres argumentos de toMap() que incluye una función de fusión.
El siguiente fragmento muestra cómo manejar duplicados:
repos.stream().collect(Collectors.toMap(
Repository::name,
Function.identity(),
(existente, nuevo) -> nuevo
));El tercer parámetro es un BinaryOperator que recibe ambos valores cuando se encuentra una clave duplicada. En este ejemplo, conservamos el valor más nuevo (nuevo), pero podrías:
- Conservar el primer valor:
(existente, nuevo) -> existente - Combinar valores:
(existente, nuevo) -> fusionarRepositorios(existente, nuevo) - Lanzar una excepción:
(existente, nuevo) -> { throw new IllegalStateException("Clave duplicada"); }
Lista a Mapa preservando orden de inserción
Por defecto, Collectors.toMap() crea un HashMap, que no preserva el orden de inserción. Para mantener el orden de los elementos de la lista original, usa un LinkedHashMap.
El siguiente fragmento muestra cómo preservar el orden de inserción:
LinkedHashMap<String, Repository> map = repos.stream().collect(Collectors.toMap(
Repository::name,
Function.identity(),
(existente, nuevo) -> existente,
LinkedHashMap::new
));El cuarto parámetro es un Supplier que proporciona la implementación del Map. LinkedHashMap mantiene una lista doblemente enlazada de entradas, preservando el orden de iteración.
Lista a Mapa manteniendo orden de claves
Para ordenar las claves alfabéticamente, usa un TreeMap que implementa SortedMap.
El siguiente fragmento muestra cómo ordenar las claves:
TreeMap<String, Repository> ordenado = repos.stream().collect(Collectors.toMap(
Repository::name,
Function.identity(),
(existente, nuevo) -> existente,
TreeMap::new
));Para orden alfabético inverso, proporciona un Comparator al constructor de TreeMap:
repos.stream().collect(Collectors.toMap(
Repository::name,
Function.identity(),
(existente, nuevo) -> existente,
() -> new TreeMap<>(Comparator.reverseOrder())
));Creando mapas inmutables
Desde Java 10, puedes usar Collectors.toUnmodifiableMap() para crear un mapa inmutable directamente.
El siguiente fragmento muestra cómo crear un mapa inmutable:
Map<String, Repository> mapaInmutable = repos.stream()
.collect(Collectors.toUnmodifiableMap(Repository::name, Function.identity()));Cualquier intento de modificar este mapa lanzará un UnsupportedOperationException. Esto es útil para crear estructuras de datos de solo lectura y garantizar la seguridad en entornos multi-hilo.
Consejo
También existe una versión con función de fusión para manejar claves duplicadas: toUnmodifiableMap(keyMapper, valueMapper, mergeFunction).
Manejo de valores nulos
Advertencia
Collectors.toMap() lanza un NullPointerException cuando la función de valor devuelve null. Esto se debe a que internamente usa HashMap.merge() que no acepta valores nulos.
// ¡Esto lanza NullPointerException si description es null!
repos.stream().collect(Collectors.toMap(Repository::name, Repository::description));Existen varias soluciones alternativas (ver ListToMapNullHandling.java para ejemplos completos):
Opción 1: Usar un bucle tradicional con HashMap.put()
Map<String, String> map = new HashMap<>();
repos.forEach(repo -> map.put(repo.name(), repo.description()));Opción 2: Reemplazar nulos con un valor por defecto
repos.stream().collect(Collectors.toMap(
Repository::name,
repo -> repo.description() != null ? repo.description() : "Sin descripción"
));Opción 3: Filtrar valores nulos antes de recoger
repos.stream()
.filter(repo -> repo.description() != null)
.collect(Collectors.toMap(Repository::name, Repository::description));Cuándo usar toMap() vs groupingBy()
Usa Collectors.toMap() cuando:
- Cada elemento se mapea a exactamente un par clave-valor
- Las claves deben ser únicas (o proporcionarás una función de fusión)
Usa Collectors.groupingBy() cuando:
- Múltiples elementos comparten la misma clave
- Quieres una estructura
Map<K, List<V>>
// groupingBy: agrupa múltiples repositorios por el flag fork
Map<Boolean, List<Repository>> porFork = repos.stream()
.collect(Collectors.groupingBy(Repository::fork));Conclusión
En este artículo, te he mostrado cómo convertir un List a un Map usando Java Streams con Collectors.toMap(). Puntos clave:
- Usa la versión básica de dos argumentos de
toMap()para claves únicas - Añade una función de fusión para manejar duplicados
- Usa
LinkedHashMappara preservar el orden de inserción - Usa
TreeMappara claves ordenadas - Usa
toUnmodifiableMap()para mapas inmutables (Java 10+) - Ten cuidado con
NullPointerExceptioncuando hay valores nulos
Puedes encontrar el código fuente completo de este artículo en GitHub.

