Definition
- groupingBy(gender, minBy(comparingInt(age))) produces
Mapwith the youngest per gender.> - collectingAndThen unwraps Optional:
collectingAndThen(minBy(...), Optional::orElseThrow). - Alternative: filter by gender, then min separately. Simpler but requires two passes.
- minBy finds the minimum (youngest age); maxBy finds the maximum (oldest age).
The bank's youth mentorship program needs the youngest male and youngest female employee. Sort each gender's pile by age and pick the top card from each.
Understanding the Concepts
How do you find the youngest per gender?
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class YoungestPerGender {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Map<String, Optional<Employee>> youngest = employees.stream()
.collect(Collectors.groupingBy(
Employee::getGender,
Collectors.minBy(
Comparator.comparingInt(Employee::getAge))));
youngest.forEach((gender, opt) ->
opt.ifPresent(e -> System.out.printf(
"Youngest %s: %s, age %d%n",
gender, e.getName(), e.getAge())));
}
}
Youngest M: Amit, age 35
Youngest F: Priya, age 28
Alternative: filter then min for each gender
Optional<Employee> youngestMale = employees.stream()
.filter(e -> "M".equals(e.getGender()))
.min(Comparator.comparingInt(Employee::getAge));
Optional<Employee> youngestFemale = employees.stream()
.filter(e -> "F".equals(e.getGender()))
.min(Comparator.comparingInt(Employee::getAge));
How do you unwrap Optional from the result?
Map<String, Employee> unwrapped = employees.stream()
.collect(Collectors.groupingBy(
Employee::getGender,
Collectors.collectingAndThen(
Collectors.minBy(Comparator.comparingInt(Employee::getAge)),
Optional::orElseThrow)));
What are common mistakes?
Mistake: using maxBy instead of minBy for youngest.
// WRONG: maxBy finds the OLDEST
Collectors.maxBy(Comparator.comparingInt(Employee::getAge));
// CORRECT: minBy finds the YOUNGEST
Collectors.minBy(Comparator.comparingInt(Employee::getAge));
What about finding both youngest and oldest using teeing?
import java.util.stream.Collectors;
record AgePair(Employee youngest, Employee oldest) {}
Map<String, AgePair> pairs = employees.stream()
.collect(Collectors.groupingBy(
Employee::getGender,
Collectors.teeing(
Collectors.minBy(Comparator.comparingInt(Employee::getAge)),
Collectors.maxBy(Comparator.comparingInt(Employee::getAge)),
(min, max) -> new AgePair(
min.orElseThrow(), max.orElseThrow()))));
teeing (Java 12+) applies two downstream collectors and merges their results.
What about the edge case of a single employee?
If a gender has only one employee, that employee is both the youngest and the oldest. minBy and maxBy both return the same person.
What about using records for clean output?
record EmployeeSummary(String name, int age, String department) {
static EmployeeSummary from(Employee e) {
return new EmployeeSummary(e.getName(), e.getAge(), e.getDepartment());
}
}
What are common interview follow-up questions?
- "How do you find both youngest and oldest per gender?" Use teeing with minBy and maxBy.
- "How do you handle ties?" minBy returns the first one in encounter order.
- "How do you find youngest per department per gender?" Nest three groupingBy levels.
Visual Overview
groupingBy(gender, minBy(comparingInt(age))) ┌──────────────────────────────────────┐ │ M → [Rajesh 45, Amit 35, Suresh 58, │ │ Deepak 55, Vikram 52] │ │ minBy(age) → Amit (35) │ │ │ │ F → [Priya 28, Kavita 40, Neha 32, │ │ Ananya 29, Meera 38] │ │ minBy(age) → Priya (28) │ └──────────────────────────────────────┘
Key Takeaway
Finding the youngest male and female uses groupingBy(gender, minBy(comparingInt(age))) which produces a Map with the youngest employee per gender in one pass. The minBy downstream returns Optional, which can be unwrapped with collectingAndThen.
The alternative is two separate filter-then-min passes, one for each gender. The groupingBy approach is more concise and handles any number of gender values automatically.
Remember one sentence: groupingBy with minBy finds the extreme value per group in one pass, and minBy is for youngest (smallest age) while maxBy is for oldest.
---