Reputation: 23277
I've a stream of MetricGroup
, where:
public class MetricGroup {
private int uploadedDocs;
private long uploadedKbs;
// getters and setters
}
I need to sumarize all metrics in one single metric. I mean, I need to add all metric.uploadedDocs
into a sumMetric.uploadedDocs
and metric.uploadedKds
into a sumMetric.uploadedKbs
.
I figure out I need some kind of reduce
Stream.of(new MetricGroup(1,100), new MetricGroup(1,200))
.reduce(????);
Any ideas?
Upvotes: 6
Views: 187
Reputation: 132590
If you want to use reduction, I'd recommend making MetricGroup
be a value type, by making the fields final, adding a zero, and replacing the setters with combining methods.
public class MetricGroup {
private final int uploadedDocs;
private final long uploadedKbs;
// obvious constructor
// getters
public static final ZERO = new MetricGroup(0, 0);
public MetricGroup add(MetricGroup a, MetricGroup b) {
return new MetricGroup(a.uploadedDocs + b.upLoadedDocs,
a.uploadedKbs + b.uploadedKbs);
}
public MetricGroup uploadOneDoc(long kbs) {
return new MetricGroup(uploadedDocs + 1, uploadedKbs + kbs);
}
}
This will let you perform stream operations nicely:
MetricGroup sum = metricGroups.stream()
.reduce(MetricGroup.ZERO, MetricGroup::add);
Upvotes: 1
Reputation: 274480
You can use this overload of reduce
:
T reduce(T identity,
BinaryOperator<T> accumulator)
like this:
.reduce(new MetricGroup(0, 0),
(x, y) -> new MetricGroup(
x.getUploadedDocs() + y.getUploadedDocs()
x.getUploadedKbs() + y.getUploadedKbs()
)
)
You can also use the collect
method:
private static MetricGroup combine(MetricGroup x, MetricGroup y) {
x.setUploadedDocs(x.getUploadedDocs() + y.getUploadedDocs());
x.setUploadedKbs(x.getUploadedKbs() + y.getUploadedKbs());
return x;
}
// ....
.collect(() -> new MetricGroup(0, 0),
YourClass::combine,
YourClass::combine
)
Upvotes: 7
Reputation: 19910
As always with java streams, you don't really have to use them. I suggest creating a simple reduction helper-method:
public static MetricGroup reduce(Iterable<? extends MetricGroup> metrics){
int uploadedDocs = 0;
long uploadedKbs = 0L;
for(MetricGroup metric : metrics){
uploadedDocs += metric.getUploadedDocs();
uploadedKbs += metric.getUploadedKbs();
}
return new MetricGroup(uploadedDocs, uploadedKbs);
}
If you can't change that you start with a stream you can still use above method, by just passing a reference to the Stream.iterator()
method:
MetricGroup reduced = reduce(stream::iterator);
Upvotes: 2
Reputation: 56469
To avoid the creation of several/many MetricGroup
objects during the reduce
call, you can make two separate calls to sum the UploadedDocs
and UploadedKbs
respectively and then construct a new MetricGroup
representing the result.
int uploadedDocsSum = source.stream().mapToInt(MetricGroup::getUploadedDocs).sum();
long uploadedKbsSum = source.stream().mapToLong(MetricGroup::getUploadedKbs).sum();
MetricGroup result = new MetricGroup(uploadedDocsSum, uploadedKbsSum);
Arguably more readable as well...
Upvotes: 3
Reputation: 73281
Simply pass in a single lambda (will manipulate existing MetricGroup)
Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
.reduce((a, b) -> {
a.setUploadedDocs(a.getUploadedDocs() + b.getUploadedDocs());
a.setUploadedKbs(a.getUploadedKbs() + b.getUploadedKbs());
return a;
});
// Optional[F.MetricGroup(uploadedDocs=2, uploadedKbs=300)]
Or, to really get a new MetricGroup (without manipulating an existing one)
Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
.reduce((a, b) -> new MetricGroup(a.getUploadedDocs() + b.getUploadedDocs(), a.getUploadedKbs() + b.getUploadedKbs()));
Upvotes: 2