【问题标题】:Create a List of Employee having particular age and increase their salary using Streams in Java 8使用 Java 8 中的 Streams 创建具有特定年龄的员工列表并增加他们的薪水
【发布时间】:2022-12-14 20:18:03
【问题描述】:

下面是Employee 类。

public static class Employee {
    private String name;
    private int age;
    private int salary;
    
    // constructor, getters, setters, etc.
}

有一份员工名单。

我需要获取 age 大于或等于 32 年的员工列表,然后使他们的 salary 增加 50% 并将这些员工收集到新列表中。

样本数据:

List<Employee> el = new ArrayList<Employee>();

el.add(new Employee("A",30,3000));
el.add(new Employee("B",32,3000));
el.add(new Employee("C",33,5000));

我的尝试:

el.stream()
    .filter(i -> i.getAge() >= 32)
    .map(i -> i.getSalary() *3 / 2)
    .collect(Collectors.toList());

但这是返回一个整数类型的列表 - List&lt;Integer&gt;。相反,我希望返回的列表是 Employee 类型的列表 - List&lt;Employee&gt;

【问题讨论】:

  • 请分享您的尝试并描述您遇到的具体问题。并更改标题以反映此问题。
  • 帮助我们帮助你 - 分享一些代码。至少,我们需要查看您的 Employee 类。
  • 请阅读How to Ask

标签: java list java-stream


【解决方案1】:

更改流中元素的状态不是一个好习惯。

相反,您可以使用流过滤具有目标年龄的员工。然后使用方法Iterable.forEach()申请工资变动。

List<Employee> employeeOlder32 = el.stream()
    .filter(i -> i.getAge() >= 32)
    .toList(); // for Java 16+ or collect(Collectors.toList()) for earlier versions
        
employeeOlder32.forEach(employee -> 
    employee.setSalary(employee.getSalary() * 3 / 2)
);

边注:通常的做法是使用BigDecimal 来表示价格、薪水等(而不是intdouble)。

【讨论】:

  • 为什么不在对象流式传输时更改对象的成员字段?
  • @BasilBourque 这是个好问题。还有很多事情要考虑。让我们从基本概念开始:1. 副作用- 导致类的可变实例发生更改的操作(不一定与集合有关)。2.Pure function - 具有的功能副作用包括参数的突变 3.我们有API requirements regarding stateful behavior and side-effects
  • @BasilBourque 让我们总结一下:我们正在拨号行动(不需要返回任何东西),这是一个副作用,因此从语义上讲,它是 forEach()forEachOrdered() 的工作,旨在通过副作用,我们有接口要求关于这些操作。文档明确指出,当我们没有其他选择时,应谨慎使用它们并将其视为合适的工具。
【解决方案2】:

您无法修改流中的数据,最终您需要创建新列表并将操作数据存储在其中。

List<Employee> newList = el.stream()
         .map(f -> new Employee(f.getName(),f.getAge(), f.setSalary((f.getSalary()*3)/2)))
         .collect(Collectors.toList());

【讨论】:

    【解决方案3】:

    另外两个答案都说你不应该或不能改变正在流式传输的对象。据我所知,该指令是不正确的。我怀疑他们混淆了你的规则不应该修改结构体集合的正在流式传输,例如向源列表添加/删除对象。您可以修改内容集合的元素。

    流式传输时修改对象

    当我们流式传输列表时,在每个 Employee 对象元素上,我们调用 Employee#setSalary 以使用新计算的值进行更新。

    使用扩展符号,代码的关键位如下。

    当我们流式传输列表时,我们使用 Stream#forEach 在每个元素上运行一些代码。

    int 字段 salary 乘以 float 类型会得到 float 值。调用 Math.round 会将其转换回 int

    employees
            .stream()
            .forEach (
                    ( Employee employee ) ->
                    {
                        employee.setSalary ( 
                            Math.round( employee.getSalary () * 1.5F ) 
                        );
                    }
            )
    ;
    

    这是完整的示例,使用紧凑的表示法。

    为方便起见,我们使用 List.of 以文字语法生成不可修改的列表。

    List < Employee > employees = List.of(
            new Employee( "Alice" , 30 , 3000 ) ,
            new Employee( "Bob" , 32 , 3000 ) ,
            new Employee( "Carol" , 33 , 5000 )
    );
    System.out.println( "Before: " + employees );
    
    employees.stream().forEach( employee -> employee.setSalary( Math.round( employee.getSalary() * 1.5F ) ) );
    System.out.println( "After: " + employees );
    

    结果:

    Before: [Employee[name=Alice, age=30, salary=3000], Employee[name=Bob, age=32, salary=3000], Employee[name=Carol, age=33, salary=5000]]
    After: [Employee[name=Alice, age=30, salary=4500], Employee[name=Bob, age=32, salary=4500], Employee[name=Carol, age=33, salary=7500]]
    

    仅供参考,这是上面使用的 Employee 类。没意思。

    package work.basil.example.modstream;
    
    import java.util.Objects;
    
    public final class Employee
    {
        // Member fields.
        private String name;
        private int age;
        private int salary;
    
        // Constructor
        public Employee ( String name , int age , int salary )
        {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    
        // Accessors
        public String getName ( ) { return name; }
    
        public void setName ( final String name ) { this.name = name; }
    
        public int getAge ( ) { return age; }
    
        public void setAge ( final int age ) { this.age = age; }
    
        public int getSalary ( ) { return salary; }
    
        public void setSalary ( final int salary ) { this.salary = salary; }
    
        // `Object` overrides.
        @Override
        public boolean equals ( Object obj )
        {
            if ( obj == this ) { return true; }
            if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
            var that = ( Employee ) obj;
            return Objects.equals( this.name , that.name ) &&
                    this.age == that.age &&
                    this.salary == that.salary;
        }
    
        @Override
        public int hashCode ( )
        {
            return Objects.hash( name , age , salary );
        }
    
        @Override
        public String toString ( )
        {
            return "Employee[" +
                    "name=" + name + ", " +
                    "age=" + age + ", " +
                    "salary=" + salary + ']';
        }
    }
    

    【讨论】:

    • Alexander 的回答并没有说这是不可能的,只是说这不是一个好的做法,我同意这一点。流是一种函数式的习语,函数式编程的一个支柱是不可变数据。不变性不是 Java 流的硬性要求,但我确实认为它是一种很好的风格。
    • @JohnKugelman 感谢您的评论。我确实同意,范围广泛或复杂的改动可能不适合流。对于像这里显示的工资修改这样的小规模特定更改,我认为没有问题。我将 Stream#forEach 视为新的 for 循环。
    • employees.stream().forEach()Iterable.forEach() 的作用相同。不同之处在于后者不需要生成流来完成这项工作,并且有关于 Iterable.forEach() 的 API 要求与 Stream.forEach() 相反。
    【解决方案4】:
    List<Employee> newEmployees = el.stream().filter(emp -> emp.getAge() > 32).map(emp -> {
      emp.setSalary(emp.getSalary() * 3 / 2);
      return emp;
    }).collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 2019-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多