Java 47: Stream Collectors Deep Dive — toMap, flatMap & teeing
easy⏱ 5 mincoursejava
toMap y la función de merge
Collectors.toMap(keyFn, valueFn) lanza IllegalStateException ante claves duplicadas — una sorpresa frecuente. Pasa un tercer argumento merge para resolver colisiones: toMap(k, v, (a, b) -> a + b) suma duplicados. Un cuarto argumento elige el tipo de mapa (TreeMap::new para ordenado). Piensa siempre en las colisiones antes de usar toMap.
Map<String, Integer> byName = people.stream()
.collect(Collectors.toMap(Person::name, Person::age,
(a, b) -> a, // keep first on collision
TreeMap::new)); // sorted map
flatMap aplana streams anidados
flatMap mapea cada elemento a un stream y los concatena — convirtiendo Stream<Order> (cada uno con una List<Item>) en un Stream<Item> plano. Es la forma canónica de expandir relaciones uno-a-muchos antes de agrupar o sumar. mapMulti (Java 16) es una alternativa de menor asignación para lo mismo.
orders.stream()
.flatMap(o -> o.items().stream()) // Stream<Order> -> Stream<Item>
.mapToInt(Item::price)
.sum();
Collectors downstream y teeing
groupingBy(classifier, downstream) post-procesa cada grupo: groupingBy(Item::category, summingInt(Item::price)) da ingreso por categoría. teeing(c1, c2, merger) (Java 12) corre dos collectors sobre el mismo stream y fusiona sus resultados — p. ej. calcular conteo y suma en una pasada, luego dividir para un promedio. Así obtienes varios agregados sin re-streamear.
Aplana, agrupa y haz teeing
Dadas órdenes que tienen líneas items({category, price}): (1) flatMap a una lista plana de items. (2) groupingBy categoría, summingInt precio -> ingreso por categoría, impreso ordenado. (3) teeing: calcula precio mín y máx en una pasada e imprime el rango. (4) toMap categoría->conteo, con un merge que suma en colisión. Imprime los cuatro.
Collectors.groupingBy no es ordenado
groupingBy devuelve un HashMap sin garantía de orden. Para salida ordenada usa groupingBy(classifier, TreeMap::new, downstream), o streamea el entry set y sorted después. No asumas orden de inserción ni natural de un groupingBy simple.
Quiz: claves duplicadas en toMap
¿Qué pasa cuando Collectors.toMap(keyFn, valFn) produce dos claves iguales? Lanza IllegalStateException: Duplicate key al colectar. La forma de dos argumentos no tiene política de merge, así que los duplicados son un error duro. Usa groupingBy cuando las claves se repiten naturalmente, o pasa una función de merge a toMap para definir el comportamiento de colisión.