Objetivo: Obter um Map> associando cada cliente à lista total de produtos comprados.
Abordagem inicial: Agrupando por cliente
Map<Customer, List<Payment>> customerToPayments =
    payments.stream()
    .collect(Collectors.groupingBy(Payment::getCustomer));
Porém, queremos produtos, não pagamentos.
Tentativa 1: Mapeando diretamente os produtos
Resultado intermediário com listas aninhadas:
Map<Customer, List<List<Product>>> customerToProductsList =
    payments.stream()
    .collect(Collectors.groupingBy(
        Payment::getCustomer,
        Collectors.mapping(Payment::getProducts, Collectors.toList())
    ));
Saída contém List>, o que não é o desejado.
Solução com duas etapas: Flatten com flatMap
Achatar as listas aninhadas usando flatMap:
Map<Customer, List<Product>> customerToProducts2steps =
    customerToProductsList.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        e -> e.getValue().stream()
              .flatMap(List::stream)
              .collect(Collectors.toList())
    ));
Solução em uma única etapa (menos legível)
Mesma ideia, mas com tudo encadeado:
Map<Customer, List<Product>> customerToProducts1step = payments.stream()
    .collect(Collectors.groupingBy(Payment::getCustomer,
        Collectors.mapping(Payment::getProducts, Collectors.toList())))
    .entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        e -> e.getValue().stream()
              .flatMap(List::stream)
              .collect(Collectors.toList())
    ));
Desvantagem: Código difícil de entender.
Solução alternativa com Collectors.reducing
Usando reducing para acumular listas de produtos:
Map<Customer, List<Product>> customerToProducts = payments.stream()
    .collect(Collectors.groupingBy(Payment::getCustomer,
        Collectors.reducing(
            Collections.emptyList(),
            Payment::getProducts,
            (l1, l2) -> {
                List<Product> l = new ArrayList<>();
                l.addAll(l1);
                l.addAll(l2);
                return l;
            }
        )
    ));
Observação: Não existe um método auxiliar no Java para unir listas diretamente, o que exige escrever o BinaryOperator manualmente.
Exemplo: ProdutosPorClienteExemplo.java
 

 
    
Top comments (0)