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?
Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.
Stream of(T t) returns a sequential Stream containing a single element. Syntax : static Stream of(T t) Parameters: This method accepts a mandatory parameter t which is the single element in the Stream. Return Value: Stream of(T t) returns a sequential Stream containing the single specified element.
The groupingBy() method of Collectors class in Java are used for grouping objects by some property and storing results in a Map instance.
There are 3 ways to print the elements of a Stream in Java: forEach() println() with collect() peek()
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With