问题
To be very simple I have
class Per{
int a;
long b;
double c;
String d;
}
Let say I have 3000 Object of Type Per
and collected in a List<Per> pers
Now I want to achieve:-
- Skip if object is null or
d
isnull
orblank
- sum of
a
- sum of
b
- aggregated value of operation performed on
c
Old way is
int totalA = 0; long totalB = 0l; long totalC = 0l;
for (Per per : pers) {
if (per.d != null && !per.d.trim().equals("")) {
totalA += per.a;
totalB += per.b;
totalC += someOperation(per.c);
}
}
someOperation
implemention is not important as it may be simple or complex.
How can I achieve this via Java8 streams and lambda expression?
A possible answer would be like this
int totalA = 0; long totalB=0l;
pers.stream().filter(p->{
if(p == null || p.d == null || p.d.trim().equals(""))
return false;
return true;
}).forEach(per->{
totalA += per.a;
totalB += per.b;
});
But totalA and totalB must be final or effectively final
Other possibilities in my mind are:
1 re-use the filtered stream using Supplier
as suggested then apply map and aggregation operations.
2 Collect the filtered Per
and then do forEach
NOTE I'm not doing any kind of grouping here and can't create new Per
or update provided objects.
回答1:
Write your own Accumulator
class and a custom Collector
, then it's a simple task.
class Accumulator {
private int a = 0;
private long b = 0L;
private double c = 0d;
public void accumulate(Per p) {
a += p.a;
b += p.b;
c += someOperation(p.c);
}
public Accumulator combine(Accumulator acc) {
a += acc.a;
b += acc.b;
c += acc.c;
return this;
}
// getter, hashCode, ...
}
Accumulator result = pers.stream()
.filter(p -> p != null && p.d != null && !p.d.trim().isEmpty())
.collect(Collector.of(Accumulator::new, Accumulator::accumulate, Accumulator::combine));
来源:https://stackoverflow.com/questions/42161085/aggregating-more-than-two-properties-java-8