Back to Stream: map & Transform
Q33Question 33 of 34

Find the average salary of male and female employees separately into Map<String, Double>

Key Concepts
  • groupingBy(gender, averagingDouble(salary)) produces Map<String, Double> of average salary per gender
  • averagingDouble handles division internally and returns 0.0 for an empty group
  • The result is a single-pass computation that is parallel-safe
  • For exact money calculations, reduce with BigDecimal and divide with an explicit rounding mode
  • Key by the gender enum name for readable output, or by the enum itself for type safety
  • Filter or map null genders to an UNKNOWN bucket to avoid corrupting the grouping

Definition

groupingBy(getGender, averagingDouble(getSalary)) groups employees by gender and computes the average salary per group in a single pass.
  • groupingBy(gender, averagingDouble(salary)) produces Map of 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?

JAVA
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));
    }
}
TEXT
M: Rs.1,300,000
F: Rs.1,000,000

How does averagingDouble handle empty groups?

JAVA
// 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?

JAVA
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?

JAVA
// 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.

JAVA
// 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.

JAVA
// 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?

JAVA
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

DIAGRAM
  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.

---

Share
Use ← → arrow keys to navigate