I have stream of objects. I want to group them and calculate the division of 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 map like {TYPE1= (0.66), TYPE2 = (0.875)}, in above example. I cannot use for loops or forEach function in stream.
CodePudding user response:
1. Using Collectors.teeing
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)
));
}
2 Using Collectors.reducing Collectors.collectingAndThen
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.
- The first argument of the reducing collector is the identity value (
new Pair(0, 0)
). - The second argument maps an instance of
TestRecord
to aPair
. - The final argument merges two Pair instances by summing the left and right value (which is
l
andr
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
)));
}