【问题标题】:How to rewrite the ValueMapper Function using java lambda如何使用 java lambda 重写 ValueMapper 函数
【发布时间】:2018-12-02 11:20:59
【问题描述】:

是否可能/正确(to)或使用lambda重写下面的内容?这里我提供了 KeyMapper 和 ValueMapper Function 的内联实现。

public Map<Integer, List<Employee>> getSubordinateHighestSalEmpMapV1(List<Employee> employees) {

        return employees.stream()
        .filter(e -> e.getSubordinates() != null)
        .collect(Collectors.toMap( //keyMapper
         new Function<Employee, Integer>() {

            @Override
            public Integer apply(Employee t) {
                return t.getId();
            }
        }, 
        new Function<Employee, List<Employee>>() {//valueMapper

            @Override
            public List<Employee> apply(Employee t) {
                List<Employee> subordinates = t.getSubordinates();
                List<Employee> subOrdinatesListWithHighestSalary = new ArrayList<>();
                int maxSal = Integer.MIN_VALUE;
                for(Employee s: subordinates) {
                    if(s.getSalary() >= maxSal) {
                        maxSal = s.getSalary();
                    }
                }
                for(Employee s: subordinates) {
                    if(s.getSalary() == maxSal) {
                        subOrdinatesListWithHighestSalary.add(s);
                    }
                }
                return subOrdinatesListWithHighestSalary;
            }
        }));
    }

我要达到什么目标:

员工类有List&lt;Employee&gt; subordinates。我试图在每个员工的下属中获得最高薪水。每个员工可能有也可能没有下属。如果下属不存在,则不包括在结果中。如果多个下属具有相同的最高薪水,则所有下属都应出现在结果中。

例如,在每个部门中获得最高薪的员工(员工,如果薪水匹配)是类似的。

Employee.java

import java.util.List;

public class Employee{

    private int id;
    private int salary;
    private List<Employee> subordinates;
    private String name;
    private int age;

    public int getId() {
        return id;
    }
    public Employee setId(int id) {
        this.id = id;
        return this;
    }
    public int getSalary() {
        return salary;
    }
    public Employee setSalary(int salary) {
        this.salary = salary;
        return this;
    }
    public List<Employee> getSubordinates() {
        return subordinates;

    }
    public Employee setSubordinates(List<Employee> subordinates) {
        this.subordinates = subordinates;
        return this;
    }
    public String getName() {
        return name;
    }
    public Employee setName(String name) {
        this.name = name;
        return this;
    }
    public int getAge() {
        return age;
    }
    public Employee setAge(int age) {
        this.age = age;
        return this;
    }
    @Override
    public String toString() {
        return "Employee [id=" + id + ", salary=" + salary  + ", name=" + name
                + ", age=" + age + "]";
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (id != other.id)
            return false;
        return true;
    }
}

例如下面的输入:

  • employee1(id:100) 有employee2,employee3,employee4 和其中 employee3支付最高的30000,应该是产出的一部分
  • employee2(id:101) 有employee5、employee6,其中employee5 的薪水最高,20000,应该是其中的一部分输出
  • employee3(id:102) 得到了employee7 和employee8,两者的工资相同16000输出应该两者都包含
  • employee8(id:107) 有一个下属employee9,工资为12000,employee9 应该是输出的一部分

    下面的输入如上所述:

     private static List<Employee>  getEmployeeListV1() {
        int i = 100;
        Employee employee1 = (Employee) new Employee().setId(i++).setSalary(10000).setAge(101).setName("emp 1");
        Employee employee2 = (Employee) new Employee().setId(i++).setSalary(20000).setAge(110).setName("emp 2");
        Employee employee3 = (Employee) new Employee().setId(i++).setSalary(30000).setAge(20).setName("emp 3");
        Employee employee4 = (Employee) new Employee().setId(i++).setSalary(10000).setAge(32).setName("emp 4");
        Employee employee5 = (Employee) new Employee().setId(i++).setSalary(20000).setAge(34).setName("emp 5");
        Employee employee6 = (Employee) new Employee().setId(i++).setSalary(15000).setAge(44).setName("emp 6");
        Employee employee7 = (Employee) new Employee().setId(i++).setSalary(16000).setAge(56).setName("emp 7");
        Employee employee8 = (Employee) new Employee().setId(i++).setSalary(16000).setAge(65).setName("emp 8");
        Employee employee9 = (Employee) new Employee().setId(i++).setSalary(12000).setAge(74).setName("emp 9");
    
        employee1.setSubordinates(Stream.of(employee2,employee3,employee4).collect(Collectors.toList()));
        employee2.setSubordinates(Stream.of(employee5,employee6).collect(Collectors.toList()));
        employee3.setSubordinates(Stream.of(employee7,employee8).collect(Collectors.toList()));
        employee8.setSubordinates(Stream.of(employee9).collect(Collectors.toList()));
    
        List<Employee> employees = Stream.of(employee1,employee2,
                employee3,employee4,employee5,
                employee6,employee7,employee8,
                employee9).collect(Collectors.toList());
        return employees;
    
    }
    

以下是输出:

100=[Employee [id=102, salary=30000, name=emp 3, age=20]]
101=[Employee [id=104, salary=20000, name=emp 5, age=34]]
102=[Employee [id=106, salary=16000, name=emp 7, age=56], Employee [id=107, salary=16000, name=emp 8, age=65]]
107=[Employee [id=108, salary=12000, name=emp 9, age=74]]

说明:

【问题讨论】:

  • 这是一个技巧/建议,当您在 IntelliJ 等 IDE 中键入此类代码时,它会建议您所有可能的清理工作。
  • 谢谢@nullpointer,但是,不幸的是我得到了eclipse。

标签: java java-8 java-stream


【解决方案1】:

当然,您可以将 keyMapper 与方法引用(Employee::getId 或 lambda employee -&gt; employee.getId())和 valueMappert -&gt; { ...)更改为 lambda,如下所示:

return employees.stream()
                .filter(e -> e.getSubordinates() != null)
                .collect(Collectors.toMap( //keyMapper
                        Employee::getId,
                        t -> {
                            List<Employee> subordinates = t.getSubordinates();
                            List<Employee> subOrdinatesListWithHighestSalary = new ArrayList<>();
                            int maxSal = Integer.MIN_VALUE;
                            for(Employee s: subordinates) {
                                if(s.getSalary() >= maxSal) {
                                    maxSal = s.getSalary();
                                }
                            }
                            for(Employee s: subordinates) {
                                if(s.getSalary() == maxSal) {
                                    subOrdinatesListWithHighestSalary.add(s);
                                }
                            }
                            return subOrdinatesListWithHighestSalary;
                        }));

您可以更进一步,将方法简化为:

return employees.stream()
         .filter(e -> e.getSubordinates() != null)
         .collect(Collectors.toMap(Employee::getId,
                        t -> {
                            int maxSal = t.getSubordinates().stream().mapToInt(Employee::getSalary).max().orElse(Integer.MIN_VALUE);
                            return t.getSubordinates().stream().filter(x -> x.getSalary() == maxSal).collect(toCollection(ArrayList::new));

                        }));

甚至更进一步:

return employees.stream()
                .filter(e -> e.getSubordinates() != null)
                .collect(Collectors.toMap(Employee::getId, Main::apply));

假设你有这个方法:

static List<Employee> apply(Employee t) {
        List<Employee> subordinates = t.getSubordinates();
        int maxSal = subordinates.stream().mapToInt(Employee::getSalary).max().orElse(Integer.MIN_VALUE);
        return subordinates.stream().filter(x -> x.getSalary() == maxSal).collect(toCollection(ArrayList::new));
}

其中Main 指的是包含apply 辅助方法的类。

【讨论】:

  • 是的。这正是我想要的。
  • 我喜欢使用方法参考的第二种方法。看起来很干净。
  • @secretsuperstar 是的,这里也一样。通常,为了简洁和可读性(主观),我总是将 lambda 表达式更改为方法引用。
【解决方案2】:

这是一个替代方案,其中值映射器需要单个流管道。

它将下属按薪水分组为TreeMap,按薪水降序排列,因此TreeMap的第一个值是薪水最高的下属列表。

public Map<Integer, List<Employee>> getSubordinateHighestSalEmpMapV1(List<Employee> employees) {

    return employees.stream()
                    .filter(e -> e.getSubordinates() != null)
                    .collect(Collectors.toMap(Employee::getId,
                                              e -> e.getSubordinates ().stream ()
                                                                       .collect (Collectors.groupingBy (Employee::getSalary,
                                                                                                        ()-> new TreeMap<>(((Comparator<Integer>)Integer::compare).reversed ()),
                                                                                                        Collectors.toList()))
                                                                       .values ()
                                                                       .iterator ()
                                                                       .next ()));

}

【讨论】:

  • 很有趣,但想知道如果下属规模很大,它是否会消耗内存。谢谢@Eran。
  • @secretsuperstar 由于此解决方案从整个下属列表创建Map&lt;Integer, List&lt;Employee&gt;&gt;,它消耗的内存与下属列表的大小相似,因此根据该列表的大小,它可能会消耗大量内存。
【解决方案3】:

虽然 Aomine 已经解释了如何优化代码,但我还是建议您在开始学习时尽可能将其编写为可读性强,因此内联将帮助您了解代码背后的实际实现写:

public Map<Integer, List<Employee>> getSubordinateHighestSalEmpMapV1(List<Employee> employees) {

    // this is your value mapper
    Function<Employee, List<Employee>> managerToSubOrdinateFunction = new Function<Employee, List<Employee>>() {
        @Override
        public List<Employee> apply(Employee employee) { // given an manager Employee
            int maxSal = employee.getSubordinates().stream() // subordinates as Stream<Employee>
                    .mapToInt(Employee::getSalary)
                    .max()// max salary amongst all subordinates
                    .orElse(Integer.MIN_VALUE);
            return employee.getSubordinates().stream()
                    .filter(s -> s.getSalary() == maxSal)// filter in only max salary subordinates
                    .collect(toList());
        }
    };

    return employees.stream()
            .filter(e -> e.getSubordinates() != null)
            .collect(Collectors.toMap(Employee::getId, managerToSubOrdinateFunction));
}

【讨论】:

  • 感谢@nullpointer。我能够(至少设法)使用内联实现编写代码。但是当我尝试使用 lambda 时,我发现它很棘手。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-19
  • 2017-10-06
相关资源
最近更新 更多