I have a stream of objects. I want to group them and calculate the division of the sum of values I get from object functions.
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;
}
}
I want to return a map like {TYPE1= (0.66), TYPE2 = (0.875)}
, in the above example. I am not allowed to use for loops or the forEach
function.
If you are using Java 12+ you can use Collectors.teeing
to solve this.
We pass two Collectors to Collectors.teeing
; the first one sums the return values of function a()
and the second for function b()
. In the merger
function of teeing, we divide the summed values.
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)
));
}
In case, you are using Java version < 12, then you can use reduction mechanism to reduce (here it is sum) the values for a given type. But this would require a simple auxiliary class.
private class Pair {
private final int l;
private final int r;
//Constructors, getters skipped
}
The reducing Collector is used from within a collectingAndThen Collector.
new Pair(0, 0)
).TestRecord
to a Pair
.l
and r
here).Finally, after reducing, we would have a single Pair
instance whose l
is equal to the sum of all a()
function values and r
is the sum of all b()
function values (for a given type
). We divide these two in the finisher of the collectingAndThen
Collector.
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
)));
}