获得 Java 流 api 的平均工资;

get average salary with Java stream api;

如何获取工资超过salaryLimit的公司员工的平均工资? 我想首先我需要获得所有薪水的列表并对其进行过滤+使用 mapToInt 和平均值,但是......没有任何效果((

public class TestCl {

@Data
@AllArgsConstructor
public class Company {
    private List<Department> departments;
}

@Data
@AllArgsConstructor
public class Department {
    private List<Employee> employees;
}

@Data
@AllArgsConstructor
public class Employee {
    private int salary;
}

@Test
public void test() {
    int salaryLimit = 25;

    List<Employee> employees = new ArrayList<>(){{
        add(new Employee(10));
        add(new Employee(20));
        add(new Employee(30));
        add(new Employee(40));
        add(new Employee(50));
    }};

    List<Department> departments = new ArrayList<>(){{
        add(new Department(employees));
        add(new Department(employees));
        add(new Department(employees));
        add(new Department(employees));
        add(new Department(employees));
    }};

    Company company = new Company(departments);

    float average = company.getDepartments().stream()
            .map(Department::getEmployees).toList().stream()
            .map(Employee::getSalary).toList().stream()
            .filter(x -> x >= salaryLimit).mapToInt().average();

    System.out.println(average);
}

}

一方面,您正在寻找 Stream.flatMap()。例如,

company.getDepartments().stream()
            .flatMap(d -> d.getEmployees().stream()) // ...

这会将您的 Departments 流转换为每个部门的 Employees 流。然后,您可以 map() 将这些添加到薪水并像您已经做的那样进行过滤。

组合 .toList().stream() 毫无意义,但是,因为它将一个流转换为另一个提供完全相同对象的流。删除它只有好处。

要映射到 IntStream 或者,可能更好地映射到 DoubleStreammapToInt()mapToDouble() 方法需要一个函数来表达映射,您忽略了它。

最后,IntStream.average()DoubleStream.average() return一个OptionalInteger / OptionalDouble / etc,它适应了由于流提供零元素而没有平均值的可能性。你需要处理它。

全部加起来,那么:

float average = company.getDepartments().stream() // a stream of Departments
        .flatMap(d -> d.getEmployees().stream())  // a stream of Employees
        .map(e -> e.getSalary())                  // a stream of Integer salaries
        .filter(s -> s.intValue() >= salaryLimit) // only the high salaries
        .mapToDouble(Integer::doubleValue)        // as a DoubleStream
        .average()                                // reduce to an average
        .getAsDouble();                           // the value, if it exists

请注意,如果没有薪水满足过滤条件,getAsDouble() 将抛出 NoSuchElementException。捕获和处理是处理这种情况的一种完全合理的方法,但是如果你想避免这种情况,那么你可以存储 average() 的结果而不是链接 getAsDouble(),然后用 [= 测试它29=] 来决定是继续 getAsDouble() 还是以不同的方式处理这种情况。或者,如果您想在这种情况下提供一个特定的 double 值——例如 0Double.NaN——那么您可以链接 orElse(valueForZeroSalaries) 而不是getAsDouble().

您得到的是编译器错误,因为 mapToInt 需要 ToIntFunction 但您提供的是 none.

此外,您不需要将流收集到列表中,只是为了再次将其转换为流。只要您不调用终端方法,您仍然可以使用您的流。

所以你可以做的是这样的:

double average = company.getDepartments().stream()
        .flatMap(department -> department.getEmployees().stream())
        .map(Employee::getSalary)
        .filter(x -> x >= salaryLimit)
        .mapToDouble(salary -> salary)
        .average()
        .orElse(0.0);

average() 给你一个 Optional<Double> 所以你需要在最后解包值。作为默认值,您可以 return 0.0 调用 orElse