Transformando Listas em Mapas com Collectors.toMap() no Java 8

Quando precisamos acessar objetos por identificadores únicos em coleções, a conversão de listas para mapas oferece eficiência significativa. Considere a seguinte estrutura de entidade:

@Data
public class Funcionario {
    private long codigo;
    private String nome;

    public Funcionario(long codigo, String nome) {
        this.codigo = codigo;
        this.nome = nome;
    }
}

Para demonstrar a transformação, criamos uma lista de funcionários:

List<Funcionario> equipe = Arrays.asList(
    new Funcionario(1001, "Ana"),
    new Funcionario(1002, "Bruno"),
    new Funcionario(1003, "Carla"),
    new Funcionario(1004, "Daniel"),
    new Funcionario(1005, "Eduarda")
);

Em abordagens tradicionais, buscar elementos específicos exigiria iteração manual:

Funcionario funcionario1002 = null;
Funcionario funcionario1004 = null;

for (Funcionario f : equipe) {
    if (f.getCodigo() == 1002) funcionario1002 = f;
    if (f.getCodigo() == 1004) funcionario1004 = f;
}

Uma solução intermediária envolve criar um mapa manualmente:

Map<Long, Funcionario> mapaFuncionarios = new HashMap<>();
for (Funcionario f : equipe) {
    mapaFuncionarios.put(f.getCodigo(), f);
}

A API Stream do Java 8 simplifica esse processo através do método Collectors.toMap():

Map<Long, Funcionario> mapa = 
    equipe.stream().collect(Collectors.toMap(
        Funcionario::getCodigo, 
        funcionario -> funcionario
    ));

Essa abordagem permite variações flexíveis conforme necessidades:

// Mapeamento para objeto completo
Map<Long, Funcionario> mapaCompleto = 
    equipe.stream().collect(Collectors.toMap(
        Funcionario::getCodigo, 
        f -> f
    ));

// Extração de atributos específicos
Map<Long, String> nomesPorCodigo = 
    equipe.stream().collect(Collectors.toMap(
        Funcionario::getCodigo, 
        Funcionario::getNome
    ));

// Transformação de valores
Map<Long, String> nomesFormatados = 
    equipe.stream().collect(Collectors.toMap(
        Funcionario::getCodigo, 
        f -> f.getNome().toUpperCase() + "_FUNC"
    ));

Quando identificadores duplicados estão presentes, o coletor requer uma função de mesclagem:

List<Funcionario> equipeDuplicada = Arrays.asList(
    new Funcionario(1001, "Ana"),
    new Funcionario(1002, "Bruno"),
    new Funcionario(1004, "Carla"),
    new Funcionario(1004, "Daniel"), // Código repetido
    new Funcionario(1005, "Eduarda")
);

// Prioriza primeiro valor encontrado
Map<Long, Funcionario> primeiroValor = 
    equipeDuplicada.stream().collect(Collectors.toMap(
        Funcionario::getCodigo,
        f -> f,
        (anterior, atual) -> anterior
    ));

// Prioriza último valor encontrado
Map<Long, Funcionario> ultimoValor = 
    equipeDuplicada.stream().collect(Collectors.toMap(
        Funcionario::getCodigo,
        f -> f,
        (anterior, atual) -> atual
    ));

// Concatena valores duplicados
Map<Long, String> valoresConcatenados = 
    equipeDuplicada.stream().collect(Collectors.toMap(
        Funcionario::getCodigo,
        Funcionario::getNome,
        (anterior, atual) -> anterior + " | " + atual
    ));

Para lidar com valores nulos nos atributos, aplicamos tratamento prévio:

List<Funcionario> equipeComNulos = Arrays.asList(
    new Funcionario(1001, "Ana"),
    new Funcionario(1002, null) // Nome nulo
);

Map<Long, String> comTratamentoNulos = 
    equipeComNulos.stream().collect(Collectors.toMap(
        Funcionario::getCodigo,
        f -> Optional.ofNullable(f.getNome()).orElse("")
    ));

Tags: Java 8 Stream API Collectors Lambda Expressions

Publicado em 9-25 00:33