Definition
- groupingBy(gender, averagingDouble(salary)) produces
Mapof average per gender. - averagingDouble handles the sum and division internally, returning 0.0 for empty groups.
- Single-pass and parallel-safe computation.
- Precision: averaging as double is fine for display; use BigDecimal for authoritative figures.
- Null genders: filter out or map to an explicit UNKNOWN bucket.
The National Bank of India pay-equity report compares male and female average salaries. Priya groups employees by gender and calculates the average salary in each group, flagging any significant disparity.
Understanding the Concepts
What does this question ask?
The interviewer wants average salary per gender using groupingBy with averagingDouble. This tests downstream collectors, gender grouping, and precision awareness.
How do you compute average salary by gender?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class AverageSalaryByGender {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Map<String, Double> avgByGender = employees.stream()
.collect(Collectors.groupingBy(
Employee::getGender,
Collectors.averagingDouble(
e -> e.getSalary().doubleValue())));
avgByGender.forEach((gender, avg) ->
System.out.printf("%s: Rs.%,.0f%n", gender, avg));
}
}
M: Rs.1,300,000
F: Rs.1,000,000
How does averagingDouble handle empty groups?
// If no employees match a gender, that key won't exist in the map
// averagingDouble returns 0.0 for an empty group if the key exists
// To guarantee both genders, use partitioningBy instead
Map<Boolean, Double> partition = employees.stream()
.collect(Collectors.partitioningBy(
e -> "M".equals(e.getGender()),
Collectors.averagingDouble(
e -> e.getSalary().doubleValue())));
System.out.println("Male avg: Rs." + partition.get(true));
System.out.println("Female avg: Rs." + partition.get(false));
How do you use exact BigDecimal arithmetic?
import java.math.BigDecimal;
import java.math.RoundingMode;
Map<String, BigDecimal> exactAvg = employees.stream()
.collect(Collectors.groupingBy(
Employee::getGender,
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
BigDecimal sum = list.stream()
.map(Employee::getSalary)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return sum.divide(
BigDecimal.valueOf(list.size()),
2, RoundingMode.HALF_UP);
})));
exactAvg.forEach((gender, avg) ->
System.out.println(gender + ": Rs." + avg));
How is this different from summarizingDouble?
| Downstream | Returns | Provides |
|---|---|---|
| averagingDouble | Double (average) | Just the average |
| summarizingDouble | DoubleSummaryStatistics | Count, sum, min, max, average |
Use summarizingDouble when you need multiple metrics per group.
What about handling null genders?
// WRONG: null key can corrupt HashMap grouping
// groupingBy throws NPE for null classifier result
// CORRECT: map null to a default
Map<String, Double> safe = employees.stream()
.collect(Collectors.groupingBy(
e -> e.getGender() != null ? e.getGender() : "UNKNOWN",
Collectors.averagingDouble(
e -> e.getSalary().doubleValue())));
What are common mistakes?
Mistake: passing BigDecimal directly to averagingDouble.
// WRONG: averagingDouble expects ToDoubleFunction, not BigDecimal
// Collectors.averagingDouble(Employee::getSalary); // compile error
// CORRECT: convert to double
Collectors.averagingDouble(e -> e.getSalary().doubleValue());
Mistake: assuming double arithmetic is exact for money.
// Double arithmetic has floating-point drift
double a = 0.1 + 0.2;
System.out.println(a); // 0.30000000000000004
// For audited figures, use BigDecimal
What about computing the pay gap?
Double maleAvg = avgByGender.getOrDefault("M", 0.0);
Double femaleAvg = avgByGender.getOrDefault("F", 0.0);
double gap = maleAvg - femaleAvg;
System.out.printf("Pay gap: Rs.%,.0f (%.1f%%)%n",
gap, (gap / maleAvg) * 100);
What are common interview follow-up questions?
- "How do you compute average per department per gender?" Nest groupingBy(dept, groupingBy(gender, averaging)).
- "How do you find the gender with the higher average?" Stream the entrySet and max by value.
- "How do you include the count alongside the average?" Use summarizingDouble instead.
Visual Overview
groupingBy(gender, averagingDouble(salary))
┌──────────────────────────────────────────┐
│ M → [1800K, 1400K, 1100K, 1500K, 1300K]│
│ sum = 7,100K / 5 = Rs.1,420,000 │
│ │
│ F → [1000K, 1200K, 900K, 800K, 1100K] │
│ sum = 5,000K / 5 = Rs.1,000,000 │
└──────────────────────────────────────────┘
Result: { M: 1420000.0, F: 1000000.0 }
Key Takeaway
Finding the average salary per gender uses groupingBy with the gender field as the classifier and averagingDouble as the downstream. The collector handles the sum and division internally in a single pass, and it is parallel-safe.
For display and analytics, the double average is fine. For authoritative financial figures, use BigDecimal with an explicit rounding mode to avoid floating-point drift. Handle null genders by filtering or mapping to an explicit UNKNOWN bucket.
At the National Bank of India, this backs a pay-equity report comparing male and female averages, with the gap computed as a percentage.
Remember one sentence: groupingBy(gender, averagingDouble(salary)) computes per-gender averages in one pass, and use BigDecimal for audited figures.
---