Coding problem · Medium · Language Idioms

Reporting with Streams and Collectors

Medium Language practicals Most askedSeen at Amazon, Goldman Sachs, JPMorgan, Atlassian

Given record Employee(String name, String dept, double salary), implement a Reports class with static methods:

  1. Map<String, Long> countByDepartment(List<Employee> employees)
  2. Map<String, Double> averageSalaryByDepartment(List<Employee> employees)
  3. List<String> topEarners(List<Employee> employees, int n) — names of the n highest salaries, highest first
  4. Map<String, List<String>> namesByDepartment(List<Employee> employees) — names sorted alphabetically per department

Use streams and Collectors rather than manual loops.

Hints

Hint 1

Collectors.groupingBy(Employee::dept, Collectors.counting())

Hint 2

Downstream collectors: averagingDouble, mapping(..., toList()), collectingAndThen to sort.

Write and run your solution

Loading the editor… (JavaScript is required to run code in your browser.)

Solution and explanation

Show the solution

Time O(n log n) where sorting is involved · Space O(n)

groupingBy(classifier, downstream) is the workhorse: the downstream collector decides what each group becomes — a count, an average, or a mapped list. collectingAndThen post-processes each group (here, sorting names). Stream.toList() (Java 16+) returns an unmodifiable list. Records give you constructors, accessors, equals/hashCode and toString for free.

Reference solution

record Employee(String name, String dept, double salary) {}

class Reports {
    static Map<String, Long> countByDepartment(List<Employee> employees) {
        return employees.stream().collect(Collectors.groupingBy(Employee::dept, Collectors.counting()));
    }

    static Map<String, Double> averageSalaryByDepartment(List<Employee> employees) {
        return employees.stream().collect(Collectors.groupingBy(Employee::dept, Collectors.averagingDouble(Employee::salary)));
    }

    static List<String> topEarners(List<Employee> employees, int n) {
        return employees.stream()
            .sorted(Comparator.comparingDouble(Employee::salary).reversed())
            .limit(n)
            .map(Employee::name)
            .toList();
    }

    static Map<String, List<String>> namesByDepartment(List<Employee> employees) {
        return employees.stream().collect(Collectors.groupingBy(
            Employee::dept,
            Collectors.collectingAndThen(
                Collectors.mapping(Employee::name, Collectors.toList()),
                names -> names.stream().sorted().toList())));
    }
}

Common mistakes

  • Using Comparator.comparing(Employee::salary) on a primitive without comparingDouble (boxing).
  • Assuming groupingBy returns a sorted map — wrap with TreeMap::new if order matters.

Follow-up questions

  • Partition by salary threshold with partitioningBy.
  • Use a TreeMap supplier for sorted departments.