1

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 is null or blank
  • 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.

Community
  • 1
  • 1
Ashwani Kumar
  • 663
  • 3
  • 6
  • 18
  • what is the reason you cant create a new Per object? Because if you could, the `reduce` method could be really useful – Ash Feb 10 '17 at 14:15

1 Answers1

6

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));
Flown
  • 11,480
  • 3
  • 45
  • 62
  • There is a way to use `Predicate` in filter: `((Predicate) Objects::nonNull).and(per -> Objects.nonNull(per.d).and(p -> p.d.trim().isEmpty()))` – Anton Balaniuc Feb 10 '17 at 14:40
  • 2
    @AntonBalaniuc You're right, but writing `p != null` is more concise than target type casting. – Flown Feb 10 '17 at 14:47