如何使用java 8的reduce函数计算平均值

问题描述 投票:0回答:1

我正在研究

java 8
概念。我遇到了
reduce
api 的
stream
方法。在一些教程中,我发现有一些内置的
reduce
方法可用于完成某些工作,例如
sum
average
等。

我不想使用这些内置方法,这样我就可以理解

reduce
方法实际上是如何工作的。

从员工列表中,我想计算那些具有 A 级员工的平均工资。

所以我写了下面的代码,其中既有经典的java代码又有java 8代码。我想知道,如何使用

reduce
方法而不是像
average
这样的内置方法。

代码:

class TestReduce{
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    private class Employee{
        int id;
        String name;
        String grade;
        double salary;
    }
    private static List<Employee> getEmployees(){
        List<Employee> list = Stream.of(
                new TestReduce().new Employee(101, "mango", "A", 60000.0),
                new TestReduce().new Employee(102, "apple", "B", 15000.0),
                new TestReduce().new Employee(103, "carrot", "C", 10000.0),
                new TestReduce().new Employee(104, "reddish", "A", 70000.0),
                new TestReduce().new Employee(105, "potato", "B", 20000.0),
                new TestReduce().new Employee(106, "okra", "C", 15000.0),
                new TestReduce().new Employee(107, "tomato", "A", 80000.0)
                ).collect(Collectors.toList());
        return list;
    }
    
    static void test0() {
        List<Employee> list = getEmployees();
        double sum = 0.0;
        int count = 0;
        for(Employee emp:list) {
            if(emp.getGrade().equals("A")) {
                count++;
                sum = sum+emp.getSalary();
            }
        }
        

System.out.println("CLASSIC approach: average salary is grade A employees : "+sum/count);


        double avg =  getEmployees().stream()
.filter(emp->emp.getGrade().equalsIgnoreCase("A"))
.map(emp->emp.getSalary())
.mapToDouble(i->i)
.average()
.getAsDouble();
        

System.out.println("JAVA 8 approach: average salary is grade A employees : "+avg);
    }
}
java java-8
1个回答
0
投票

如何使用

reduce
方法而不是像
average
这样的内置方法。

这个特殊情况有点棘手,因为要平均,您需要同时累积两个不同的值,然后最后将它们组合起来。 Java 的通用

Stream.reduce()
DoubleStream.reduce()
方法并不直接支持这一点。

但是您可以将流简化为包含元素计数和总和的二元素列表或数组,然后计算平均值。或者,您可以通过引入一个自定义类来聚合结果,从而使其更加流畅。例如:

static class Averager {
    private long elementCount = 0;
    private double elementSum = 0.0;

    public double getAverage() {
        return elementCount == 0 ? Double.NaN : elementSum / elementCount;
    }

    public static Averager ofOneElement(double d) {
        Averager a = new Averager();
        a.elementCount = 1;
        a.elementSum = d;
        return a;
    }

    public static Averager combine(Averager a, Averager b) {
        Averager c = new Averager();
        c.elementCount = a.elementCount + b.elementCount;
        c.elementSum = a.elementSum + b.elementSum;
        return c;
    }
}

// ... the actual reduction:

        double avg =  getEmployees().stream()
            .filter(emp->emp.getGrade().equalsIgnoreCase("A"))
            .map(emp -> Averager.ofOneElement(emp.getSalary()))
            .reduce(new Averager(), Averager::combine)
            .getAverage();

当然,这比内置的 average() 减少

DoubleStream
冗长
,但这是可以预料的。

© www.soinside.com 2019 - 2024. All rights reserved.