将 Java Streams 中两个不同属性的总和相除

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

我有一个对象流。我想将它们分组并计算从对象函数获得的值之和的除法。

import java.util.Map;
import java.util.stream.Stream;

record TestRecord(String type, int a, int b) {
    public int getA() { return a; }
    public int getB() { return b; }
}
    
public class Test {    
    public static void main(String[] args) {
        Stream<TestRecord> testRecords = Stream.of(
            new TestRecord("TYPE1", 1, 2),
            new TestRecord("TYPE1", 3, 4),
            new TestRecord("TYPE2", 5, 6),
            new TestRecord("TYPE2", 7, 8),
            new TestRecord("TYPE2", 9, 10)
        );
    }
    
    //It should return {TYPE1= (4/6), TYPE2 = (21 / 24)}
    //TYPE1= (1+3 / 2+4), TYPE2 = ((5+7+9) / (6+8+10))
    public static Map<String, Double> myFunction(Stream<TestRecord> testRecordStream) {
        //TODO
        return null;
    }
}

我想返回一个像上例中的

{TYPE1= (0.66), TYPE2 = (0.875)}
这样的地图。我不允许使用 for 循环或
forEach
函数。

java java-stream collectors
1个回答
3
投票

1.使用 Collectors.teeing

如果您使用的是 Java 12+,您可以使用

Collectors.teeing
来解决此问题。

我们将两个Collectors传递给

Collectors.teeing
;第一个对函数
a()
的返回值求和,第二个对函数
b()
的返回值求和。在 teeing 的
merger
函数中,我们将求和值相除。

public static Map<String, Double> myFunction(Stream<TestRecord> testRecordStream) {
    return testRecordStream
            .collect(Collectors.groupingBy(TestRecord::type,
                    Collectors.teeing(
                            Collectors.summingDouble(TestRecord::a),
                            Collectors.summingDouble(TestRecord::b),
                            (a, b) -> a / b)
            ));
}

2 使用 Collectors.reducing + Collectors.collectingAndThen

如果您正在使用 Java 版本的 < 12, then you can use reduction 机制来减少(这里是总和)给定类型的值。但这需要一个简单的辅助类。

private class Pair {
    private final int l;
    private final int r;
    //Constructors, getters skipped
}

reducing收集器在collectingAndThen收集器中使用。

  • 归约收集器的第一个参数是身份值
    new Pair(0, 0)
    )。
  • 第二个参数将
    TestRecord
    的实例映射到
    Pair
  • 最后一个参数通过求和左右值来合并两个 Pair 实例(这里是
    l
    r
    )。

最后,在归约之后,我们将得到一个

Pair
实例,其
l
等于所有
a()
函数值的总和,而
r
是所有
b()
函数值的总和(对于给定的
 type
)。我们在 collectingAndThen 收集器的
finisher
中划分这两个。

public static Map<String, Double> myFunction(Stream<TestRecord> testRecordStream) {
    return testRecordStream
            .collect(Collectors.groupingBy(TestRecord::type,
                    Collectors.collectingAndThen(
                            Collectors.reducing(new Pair(0, 0),
                                    testRecord -> new Pair(testRecord.a(), testRecord.b()),
                                    (p1, p2) -> new Pair(p1.l + p2.l, p1.r + p2.r)),
                            pair -> (double) pair.l / pair.r
                    )));
}
© www.soinside.com 2019 - 2024. All rights reserved.