In the Employee
class below, I would like to get average salary
, average bonus
and average perks
for all employees grouping by department
, designation
and gender
and would like the result to be a List<Employee>
with the aggregated values for salary
, bonus
and perks
.
public class Employee { private String name; privte String department; private String gender; private String designation; private Integer salary; private Integer bonus; private Integer perks; }
What would be a clean way of doing that?
Advertisement
Answer
You can do this by creating a class for the grouping key and writing a collector:
I’m simply ading the values per key and count the occurances in a map. In the finisher I devide the sums through the count.
You could get rid of the countMap by sublassing Employee, adding the count and using this class for the supplier/subtotal and using some casting…
You could also make to groupBys one for the sum and another for the count and computing the avarages with the two created maps…
public class Employee { private String name; private String department; private String gender; private String designation; private Integer salary; private Integer bonus; private Integer perks; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getDesignation() { return designation; } public void setDesignation(String designation) { this.designation = designation; } public Integer getSalary() { return salary; } public void setSalary(Integer salary) { this.salary = salary; } public Integer getBonus() { return bonus; } public void setBonus(Integer bonus) { this.bonus = bonus; } public Integer getPerks() { return perks; } public void setPerks(Integer perks) { this.perks = perks; } public Employee(String name, String department, String gender, String designation, Integer salary, Integer bonus, Integer perks) { super(); this.name = name; this.department = department; this.gender = gender; this.designation = designation; this.salary = salary; this.bonus = bonus; this.perks = perks; } public Employee() { super(); } public static void main(String[] args) { List<Employee> values = new ArrayList<>(); values.add(new Employee("bill", "dep1", "male", "des1", 100000, 5000, 20)); values.add(new Employee("john", "dep1", "male", "des1", 80000, 4000, 10)); values.add(new Employee("lisa", "dep1", "female", "des1", 80000, 4000, 10)); values.add(new Employee("rosie", "dep1", "female", "des2", 70000, 3000, 15)); values.add(new Employee("will", "dep2", "male", "des1", 60000, 3500, 18)); values.add(new Employee("murray", "dep2", "male", "des1", 70000, 3000, 13)); Map<EmployeeGroup, Employee> resultMap = values.stream().collect(Collectors.groupingBy(e-> new EmployeeGroup(e) , new EmployeeCollector())); System.out.println(new ArrayList(resultMap.values())); } @Override public String toString() { return "Employee [name=" + name + ", department=" + department + ", gender=" + gender + ", designation=" + designation + ", salary=" + salary + ", bonus=" + bonus + ", perks=" + perks + "]"; } }
Class for the aggregating key
public class EmployeeGroup { private String department; private String gender; private String designation; public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getDesignation() { return designation; } public void setDesignation(String designation) { this.designation = designation; } public EmployeeGroup(Employee employee) { this.department = employee.getDepartment(); this.gender = employee.getGender(); this.designation = employee.getDesignation(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((designation == null) ? 0 : designation.hashCode()); result = prime * result + ((gender == null) ? 0 : gender.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; EmployeeGroup other = (EmployeeGroup) obj; if (department == null) { if (other.department != null) return false; } else if (!department.equals(other.department)) return false; if (designation == null) { if (other.designation != null) return false; } else if (!designation.equals(other.designation)) return false; if (gender == null) { if (other.gender != null) return false; } else if (!gender.equals(other.gender)) return false; return true; } }
Collector
public class EmployeeCollector implements Collector<Employee, Employee, Employee> { private Map<EmployeeGroup,Integer> countMap = new HashMap<>(); @Override public Supplier<Employee> supplier() { return () -> new Employee(); } @Override public BiConsumer<Employee, Employee> accumulator() { return this::accumulator; } @Override public BinaryOperator<Employee> combiner() { return this::accumulator; } @Override public Function<Employee, Employee> finisher() { return e -> { Integer count = countMap.get(new EmployeeGroup(e)); e.setBonus(e.getBonus()/count); e.setPerks(e.getPerks()/count); e.setSalary(e.getSalary()/count); return e; }; } @Override public Set<Characteristics> characteristics() { return Stream.of(Characteristics.UNORDERED) .collect(Collectors.toCollection(HashSet::new)); } public Employee accumulator(Employee subtotal, Employee element) { if (subtotal.getDepartment() == null) { subtotal.setDepartment(element.getDepartment()); subtotal.setGender(element.getGender()); subtotal.setDesignation(element.getDesignation()); subtotal.setPerks(element.getPerks()); subtotal.setSalary(element.getSalary()); subtotal.setBonus(element.getBonus()); countMap.put(new EmployeeGroup(subtotal), 1); } else { subtotal.setPerks(subtotal.getPerks() + element.getPerks()); subtotal.setSalary(subtotal.getSalary() + element.getSalary()); subtotal.setBonus(subtotal.getBonus() + element.getBonus()); EmployeeGroup group = new EmployeeGroup(subtotal); countMap.put(group, countMap.get(group)+1); } return subtotal; } }