Back to Stream Practice: Employee Analysis
Q34Question 34 of 43

List departments where average age is above 35

Key Concepts
  • groupingBy(dept, averagingInt(age)) produces Map<String, Double> of average age per department
  • Filter the entrySet where value > 35 to keep only departments above the threshold
  • This is an aggregate-then-filter pattern (group, compute average, filter result map)
  • The threshold should be parameterized for reusability
  • Collectors.filtering (Java 9+) filters within groups but does not help here because the filter is on the aggregate, not individual elements
  • Map to just the keys to get a List<String> of qualifying department names

Definition

Group by department with averagingInt(age), then filter the resulting map entries where the average exceeds 35.
  • groupingBy(dept, averagingInt(age)) produces Map of average age per department.
  • entrySet filter(avg > 35) keeps only qualifying departments.
  • Aggregate-then-filter: compute the aggregate first, then filter the aggregated results.

HR wants departments whose average age exceeds 35 for succession planning. Compute the average age per department, then check which averages are above the threshold.

Understanding the Concepts

How do you find departments with average age above 35?

JAVA
package com.bank.hr;

import java.util.*;
import java.util.stream.Collectors;

public class DeptAvgAgeAbove35 {
    public static void main(String[] args) {
        List<Employee> employees = Employee.getSampleEmployees();

        Map<String, Double> avgAgeByDept = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::getDepartment,
                Collectors.averagingInt(Employee::getAge)));

        List<String> qualifying = avgAgeByDept.entrySet().stream()
            .filter(e -> e.getValue() > 35)
            .map(Map.Entry::getKey)
            .toList();

        System.out.println("Departments with avg age > 35: " + qualifying);
        avgAgeByDept.forEach((dept, avg) ->
            System.out.printf("  %s: %.1f %s%n", dept, avg,
                avg > 35 ? "✓" : ""));
    }
}
TEXT
Departments with avg age > 35: [TRADING, RISK, OPERATIONS, COMPLIANCE]
  TRADING: 38.3 (above)
  RISK: 41.0 (above)
  OPERATIONS: 45.0 (above)
  COMPLIANCE: 40.5 (above)

What are common mistakes?

Mistake: trying to filter individuals by age > 35 instead of filtering departments by average.

JAVA
// WRONG: this filters EMPLOYEES aged > 35, not departments with AVG > 35
List<Employee> wrong = employees.stream()
    .filter(e -> e.getAge() > 35)
    .toList();

// CORRECT: compute department averages first, then filter departments

What about parameterizing the threshold?

JAVA
public static List<String> deptsAboveAvgAge(
        List<Employee> employees, double threshold) {
    return employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDepartment,
            Collectors.averagingInt(Employee::getAge)))
        .entrySet().stream()
        .filter(e -> e.getValue() > threshold)
        .map(Map.Entry::getKey)
        .toList();
}

Accepting the threshold as a parameter makes the method reusable for any cutoff.

What about returning both the department name and its average?

JAVA
import java.util.AbstractMap.SimpleEntry;

List<Map.Entry<String, Double>> withAvg = avgAgeByDept.entrySet().stream()
    .filter(e -> e.getValue() > 35)
    .toList();

Return the filtered entries instead of mapping to keys only.

What about using Collectors.filtering (Java 9+)?

Collectors.filtering filters individual elements within groups, not the aggregate result. It does not help here because the filter is on the computed average, not on individual employees.

How is this different from filtering employees by age?

DIAGRAM
| Operation | What it does |
|---|---|
| filter(e -> e.getAge() > 35) | Keeps individual employees aged over 35 |
| group + averagingInt + filter(avg > 35) | Keeps departments whose average age exceeds 35 |

The question asks about departments, not individuals.

What are common interview follow-up questions?

  • "How do you parameterize the threshold?" Accept it as a method argument.
  • "How do you return both the department name and its average?" Return the filtered map entries instead of mapping to keys only.

Visual Overview

DIAGRAM
  Step 1: averagingInt(age) per department
  ┌──────────────────────────────────┐
  │  TRADING     → 38.3              │
  │  RISK        → 41.0              │
  │  OPERATIONS  → 45.0              │
  │  COMPLIANCE  → 40.5              │
  └──────────────────────────────────┘

  Step 2: filter(avg > 35)
  → All four departments qualify

Key Takeaway

Listing departments where the average age exceeds a threshold is an aggregate-then-filter pattern: compute averages per department with groupingBy and averagingInt, then filter the entrySet for averages above the threshold.

Remember one sentence: group by department, compute the average, then filter the map entries where the average exceeds the threshold.

---

Share
Use ← → arrow keys to navigate