如何在Java Streams中分组计算两个属性总和的商
Java Stream分组计算总和比值的实现
需求:给定TestRecord对象的Stream,按type字段分组,计算每组中属性a的总和与属性b的总和的商,返回Map<String, Double>格式结果(示例:{TYPE1=0.66, TYPE2=0.875})。禁止使用for循环或forEach方法。
现有代码框架:
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) ); } // 预期返回 {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; } }
实现方案
Java 12+ 简洁版本
利用Collectors.teeing同时收集两组总和,再计算比值:
import java.util.Map; import java.util.stream.Collectors; 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) ); Map<String, Double> result = myFunction(testRecords); System.out.println(result); // 输出 {TYPE1=0.6666666666666666, TYPE2=0.875} } public static Map<String, Double> myFunction(Stream<TestRecord> testRecordStream) { return testRecordStream.collect(Collectors.groupingBy( TestRecord::type, Collectors.teeing( Collectors.summingInt(TestRecord::getA), Collectors.summingInt(TestRecord::getB), (sumA, sumB) -> sumB == 0 ? 0.0 : (double) sumA / sumB ) )); } }
代码说明
- 分组逻辑:
Collectors.groupingBy(TestRecord::type)按type字段分组,键为类型字符串,值为对应组的计算结果。 - 双收集器计算:
Collectors.teeing同时用两个summingInt收集器,分别计算每组a和b的总和。 - 比值计算:合并函数中把总和转为double计算商,同时处理
sumB为0的边界情况(此处返回0.0,可按需调整为抛出异常)。
Java 8+ 兼容版本
如果使用Java 12以下版本,可通过reducing合并总和后转换结果:
public static Map<String, Double> myFunction(Stream<TestRecord> testRecordStream) { return testRecordStream.collect(Collectors.groupingBy( TestRecord::type, Collectors.reducing( new int[]{0, 0}, // 初始值:sumA=0, sumB=0 record -> new int[]{record.getA(), record.getB()}, (acc, curr) -> new int[]{acc[0] + curr[0], acc[1] + curr[1]} ) )).entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> { int[] sums = entry.getValue(); return sums[1] == 0 ? 0.0 : (double) sums[0] / sums[1]; } )); }
这个版本先通过reducing将每组元素合并为存储sumA和sumB的数组,再将结果转换为目标Map格式。
内容的提问来源于stack exchange,提问作者Bedirhan Gökce
相关产品推荐
相关产品推荐

