Transformación eficiente de colecciones a mapas en Java

Transformar una colección en un mapa es una operación común para facilitar búsquedas rápidas o la eliminación de duplicados. Existen varias alternativas en el ecosistema Java para realizar esta conversión, cada una con comportamientos específicos ante claves duplicadas. Veremos tres enfoques: usando la biblioteca Google Guava, con la utilidad Hutool y mediante el API Stream de Java.

Enfoque con Google Guava: Maps.uniqueIndex

La clase Maps de Google Guava proporciona el método estático uniqueIndex. Este método itera sobre la colección y aplica una función para extraer la clave de cada elemento. Si dos elementos producen la misma clave, el método lanza una excepción IllegalArgumentException, lo que lo hace adecuado cuando se garantiza la unicidad de las claves.

import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Map;

public class ConversionGuava {
    public static <K, V> Map<K, V> transformar(Collection<V> coleccion, Function<V, K> extractorClave) {
        return Maps.uniqueIndex(coleccion, extractorClave::apply);
    }
}

// Ejemplo de uso:
// List<Usuario> usuarios = ...;
// Map<Integer, Usuario> mapaPorId = ConversionGuava.transformar(usuarios, Usuario::obtenerId);
// Si hay IDs duplicados, se lanzará IllegalArgumentException.

Enfoque con Hutool: CollUtil.toMap

La biblioteca Hutool ofrece una alternativa flexible a través de su clase CollUtil. Su método toMap permite especificar un mapa destino, funciones para la clave y el valor. Cuando encuentran claves duplicadas, el valor posterior sobrescribe al anterior sin generar un error, por lo que el resultado final es determinista en el sentido de que conserva el último elemento procesado.

import cn.hutool.core.collection.CollUtil;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class ConversionHutool {
    public static <K, V> Map<K, V> transformar(Collection<V> coleccion, Function<V, K> extractorClave) {
        return CollUtil.toMap(coleccion, new HashMap<>(), extractorClave::apply, v -> v);
    }
}

// Ejemplo de uso:
// Set<Usuario> conjuntoUsuarios = ...;
// Map<Integer, Usuario> mapaPorId = ConversionHutool.transformar(conjuntoUsuarios, Usuario::obtenerId);
// Los elementos con claves repetidas se sobrescriben, quedando el último procesado.

Enfoque con Java Stream API: Collectors.toMap

Desde Java 8, el API Stream proporciona un colector muy potente: Collectors.toMap. Su gran ventaja es la flexibilidad para manejar colisiones de claves mediante un parámetro de función de combinación (mergeFunction). Si no se provee esta función y existen claves duplicadas, se lanzará un IllegalStateException.

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

public class ConversionStream {
    // Lanza excepción si hay claves duplicadas
    public static <K, V> Map<K, V> transformarSinDuplicados(Collection<V> coleccion, Function<V, K> extractorClave) {
        return coleccion.stream()
                .collect(Collectors.toMap(extractorClave::apply, v -> v));
    }

    // Permite definir qué hacer en caso de duplicados (ej: conservar el primero)
    public static <K, V> Map<K, V> transformarConservandoPrimero(Collection<V> coleccion, Function<V, K> extractorClave) {
        return coleccion.stream()
                .collect(Collectors.toMap(extractorClave::apply, v -> v, (valorExistente, nuevoValor) -> valorExistente));
    }
}

// Ejemplo de uso:
// List<Usuario> listaUsuarios = ...;
// Map<Integer, Usuario> mapaSinDuplicados = ConversionStream.transformarSinDuplicados(listaUsuarios, Usuario::obtenerId);
// Map<Integer, Usuario> mapaConservandoPrimero = ConversionStream.transformarConservandoPrimero(listaUsuarios, Usuario::obtenerId);

Comparación de comportamientos

Método Soporte para claves duplicadas Comportamiento ante duplicados
Maps.uniqueIndex (Guava) No Lanza IllegalArgumentException
CollUtil.toMap (Hutool) Sí (sobrescritura) Conserva el último valor insertado
Collectors.toMap (Java Stream) Sí (controlable) Depende de la función de combinación; sin ella lanza IllegalStateException

Consideraciones adicionales

Cuando la clave del mapa no se deriva de un solo campo, sino de una combinación, es necesario usar una expresión lambda en lugar de una referencia a método. Esto permite concatenar o transformar múltiples propiedades del objeto en una única clave compuesta.

// Crear una clave compuesta usando Stream API
Map<String, Usuario> mapaClaveCompuesta = listaUsuarios.stream()
        .collect(Collectors.toMap(
                usuario -> usuario.obtenerId() + "_" + usuario.obtenerNombre(),
                usuario -> usuario,
                (v1, v2) -> v1 // Política de resolución para claves compuestas idénticas
        ));

Etiquetas: Google Guava Hutool Java Stream API Collectors.toMap Maps.uniqueIndex

Publicado el 8-15 22:17