简体   繁体   中英

How to group by range of values in Java 8 using streams

Here is a sample scenario:

Imagine we have employee records like:

name, age, salary (in 1000 dollars)
   a,  20,     50
   b,  22,     53
   c,  34,     79

and so on. The goal is to calculate the average salary of different age groups (for instance between 21 and 30 and 31 to 40 and so on).

I want to do this using stream and I just cant get my head around how I need to use groupingBy to get this done. I am thinking maybe I need to define some sort of tuple age range. Any ideas?

The below code should give you what you are looking for. The key is "Collectors" class which support grouping.

Map<Double,Integer> ageGroup= employees.stream().collect(Collectors.groupingBy(e->Math.ceil(e.age/10.0),Collectors.summingInt(e->e.salary)));

The illustration assuming the salary is integer but easy to switch to double

The complete program looks like

public static void main(String[] args) {
    // TODO Auto-generated method stub

    List<Employee> employees = new ArrayList<>();
    employees.add(new Employee("a",20,100));
    employees.add(new Employee("a",21,100));
    employees.add(new Employee("a",35,100));
    employees.add(new Employee("a",32,100));


    Map<Double,Integer> ageGroup= employees.stream().collect(Collectors.groupingBy(e->Math.ceil(e.age/10.0),Collectors.summingInt(e->e.salary)));
    System.out.println(ageGroup);
}

public static class Employee {
    public Employee(String name, int age, int salary) {
        super();
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    public String name;
    public int age;
    public int salary;

}

The output is

{4.0=200, 2.0=100, 3.0=100}

Yes, you could define an AgeGroup interface or even an enum like this (assuming Employee definition):

enum AgeGroup {
    TWENTIES,
    THIRTIES,
    FORTIES,
    FIFTIES;
    .....
}
Function<Employee, AgeGroup> employee2Group = e -> {
    if(e.age >= 20 && e.getAge() < 30)
        return AgeGroup.TWENTIES;
    ....
    return null;
};

Map<AgeGroup, Double> avgByAgeGroup = employees.stream()
    .collect(Collectors.groupingBy(employee2Group, Collectors.averagingInt(Employee::getSalary)));

avgByAgeGroup.get(AgeGroup.TWENTIES)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM