如何在Java流中使用MaxBy进行分组和聚合

如何在Java流中使用MaxBy进行分组和聚合,java,java-8,java-stream,Java,Java 8,Java Stream,我有一个Employee对象列表,如下所示 public class Employee { int eno; String name; String dept; int salary; ...... } 要求是获得每个部门最高工资的员工名单。部门中可以有多个员工拥有相同的最高工资。例如,销售团队可能有两名员工,比如Alex&John,他们的工资最高 List<Employee> eList = new ArrayList<>();

我有一个
Employee
对象列表,如下所示

public class Employee {
    int eno;
    String name;
    String dept;
    int  salary;
   ......
}
要求是获得每个部门最高工资的员工名单。部门中可以有多个员工拥有相同的最高工资。例如,销售团队可能有两名员工,比如Alex&John,他们的工资最高

List<Employee> eList = new ArrayList<>();
eList .add(new Employee(101, "ALEX","SALES",7000));
eList.add(new Employee(102, "KATHY","ADMIN",3000));
eList.add(new Employee(103, "JOHN","SALES",7000));
eList.add(new Employee(104, "KONG","IT",5000));
eList.add(new Employee(105, "MIKE","SALES",4000));
List eList=new ArrayList();
添加列表(新员工(101,“ALEX”,“SALES”,7000);
eList.add(新员工(102,“KATHY”,“ADMIN”,3000));
eList.add(新员工(103,“JOHN”,“SALES”,7000));
添加(新员工(104,“香港”,“IT”,5000));
eList.add(新员工(105,“迈克”,“销售”,4000));
预期输出为->映射(部门,具有最高工资的姓名列表)

使用下面的代码,我只能得到一个Employee对象作为输出,即使有多个Employee满足该条件

Map<String, Employee> maxSalEmpNameByDept = eList.stream().collect(groupingBy(Employee::getDept, 
                                                        collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)), Optional::get)));  
Map maxSalEmpNameByDept=eList.stream().collect(groupingBy(Employee::getDept,
collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)),可选::get));
这是因为MaxBy方法在找到满足给定条件的第一项时停止

Map<String, Employee> maxSalEmpNameByDept = eList.stream().collect(groupingBy(Employee::getDept, 
                                                        collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)), Optional::get)));  
我的问题是:

  • 我们如何获得每个部门的最高薪酬员工名单(
    Map
  • 如何在每个部门单独检索薪酬最高的员工名单(
    Map

  • 首先,使用
    Collectors.toMap

    Map<String, Integer> maxSalByDept =
            eList.stream()
                 .collect(Collectors.toMap(Employee::getDept, Employee::getSalary,
                             BinaryOperator.maxBy(Comparator.comparing(Function.identity()))));
    

    首先,使用
    Collectors.toMap

    Map<String, Integer> maxSalByDept =
            eList.stream()
                 .collect(Collectors.toMap(Employee::getDept, Employee::getSalary,
                             BinaryOperator.maxBy(Comparator.comparing(Function.identity()))));
    

    如果你写自己的收藏家,这很容易。我在真正搜索之前写过这篇文章,但图书馆实际上已经有了


    如果你写自己的收藏家,这很容易。我在真正搜索之前写过这篇文章,但图书馆实际上已经有了


    这里有一种方法不使用显式的
    比较器
    或某些第三方库。我填写了你的Employee类并添加了一些额外的员工

    提供的数据

    List<Employee> eList = new ArrayList<>();
    eList.add(new Employee(101, "ALEX", "SALES", 7000));
    eList.add(new Employee(102, "KATHY", "ADMIN", 3000));
    eList.add(new Employee(103, "JOHN", "SALES", 7000));
    eList.add(new Employee(104, "KONG", "IT", 5000));
    eList.add(new Employee(106, "MIKE", "SALES", 4000));
    eList.add(new Employee(107, "BOB", "IT", 4000));
    eList.add(new Employee(108, "SARAH", "ADMIN", 2000));
    eList.add(new Employee(109, "RALPH", "SALES", 4000));
    eList.add(new Employee(110, "JUNE", "ADMIN", 3000));
    
    印刷品

    ADMIN=[{102,  KATHY,  ADMIN,  3000}, {110,  JUNE,  ADMIN,  3000}]
    IT=[{104,  KONG,  IT,  5000}]
    SALES=[{101,  ALEX,  SALES,  7000}, {103,  JOHN,  SALES,  7000}]
    
    IT=[KONG]
    ADMIN=[KATHY, JUNE]
    SALES=[ALEX, JOHN]
    
    
    要仅获取名称,请使用刚刚创建的地图

    • 流化值(员工列表)
    • 然后通过flatMap将员工列表放入一个
      Employee
      流中
    • 记住,在这一点上,这是每个部门的最高工资,所以 只需按部门对员工进行分组,并将他们的姓名收集到一个列表中
    我用构造函数、getter和toString修改了您的类

        public static class Employee {
            private int eno;
            private String name;
            private String dept;
            private int salary;
            
            public Employee(int eno, String name, String dept,
                    int salary) {
                this.eno = eno;
                this.name = name;
                this.dept = dept;
                this.salary = salary;
            }
            
            public int getEno() {
                return eno;
            }
            
            public String getName() {
                return name;
            }
            
            public String getDept() {
                return dept;
            }
            
            public int getSalary() {
                return salary;
            }
            
            @Override
            public String toString() {
                return String.format("{%s,  %s,  %s,  %s}", eno, name,
                        dept, salary);
            }
        }
        
    }
    

    这里有一种方法不使用显式的
    比较器
    或某些第三方库。我填写了你的Employee类并添加了一些额外的员工

    提供的数据

    List<Employee> eList = new ArrayList<>();
    eList.add(new Employee(101, "ALEX", "SALES", 7000));
    eList.add(new Employee(102, "KATHY", "ADMIN", 3000));
    eList.add(new Employee(103, "JOHN", "SALES", 7000));
    eList.add(new Employee(104, "KONG", "IT", 5000));
    eList.add(new Employee(106, "MIKE", "SALES", 4000));
    eList.add(new Employee(107, "BOB", "IT", 4000));
    eList.add(new Employee(108, "SARAH", "ADMIN", 2000));
    eList.add(new Employee(109, "RALPH", "SALES", 4000));
    eList.add(new Employee(110, "JUNE", "ADMIN", 3000));
    
    印刷品

    ADMIN=[{102,  KATHY,  ADMIN,  3000}, {110,  JUNE,  ADMIN,  3000}]
    IT=[{104,  KONG,  IT,  5000}]
    SALES=[{101,  ALEX,  SALES,  7000}, {103,  JOHN,  SALES,  7000}]
    
    IT=[KONG]
    ADMIN=[KATHY, JUNE]
    SALES=[ALEX, JOHN]
    
    
    要仅获取名称,请使用刚刚创建的地图

    • 流化值(员工列表)
    • 然后通过flatMap将员工列表放入一个
      Employee
      流中
    • 记住,在这一点上,这是每个部门的最高工资,所以 只需按部门对员工进行分组,并将他们的姓名收集到一个列表中
    我用构造函数、getter和toString修改了您的类

        public static class Employee {
            private int eno;
            private String name;
            private String dept;
            private int salary;
            
            public Employee(int eno, String name, String dept,
                    int salary) {
                this.eno = eno;
                this.name = name;
                this.dept = dept;
                this.salary = salary;
            }
            
            public int getEno() {
                return eno;
            }
            
            public String getName() {
                return name;
            }
            
            public String getDept() {
                return dept;
            }
            
            public int getSalary() {
                return salary;
            }
            
            @Override
            public String toString() {
                return String.format("{%s,  %s,  %s,  %s}", eno, name,
                        dept, salary);
            }
        }
        
    }
    

    嵌套分组后,可以通过这种方式避免flatMaping
    …entrySet().stream().collect(Collectors.toMap(m->m.getKey(),m->m.getValue().lastEntry().getValue())@Rono观察力极佳。我做了改变。谢谢嵌套分组后,可以通过这种方式避免flatMaping
    …entrySet().stream().collect(Collectors.toMap(m->m.getKey(),m->m.getValue().lastEntry().getValue())@Rono观察力极佳。我做了改变。谢谢具有完整的收集器,因此无需考虑是否添加合并函数。有一个完整的收集器,因此无需考虑是否添加合并功能。@Holger我想这个问题部分解决了这个问题,因为OP需要每个部门的最高工资员工名单。@Rono给出OP的原始代码,在我看来,对流API有足够的理解,可以组合一个收集器,如对
    groupingBy
    链接问题的第二个答案所示。否则,他们可能会提出一个新问题。@Holger我认为这个问题部分解决了这个问题,因为OP需要每个部门的最高工资员工名单。@Rono鉴于OP的原始代码,在我看来,对Stream API有足够的了解,可以合并一个收集器,如
    groupingBy
    链接问题的第二个答案所示。否则,他们可能会提出一个新问题。
        public static class Employee {
            private int eno;
            private String name;
            private String dept;
            private int salary;
            
            public Employee(int eno, String name, String dept,
                    int salary) {
                this.eno = eno;
                this.name = name;
                this.dept = dept;
                this.salary = salary;
            }
            
            public int getEno() {
                return eno;
            }
            
            public String getName() {
                return name;
            }
            
            public String getDept() {
                return dept;
            }
            
            public int getSalary() {
                return salary;
            }
            
            @Override
            public String toString() {
                return String.format("{%s,  %s,  %s,  %s}", eno, name,
                        dept, salary);
            }
        }
        
    }