Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 lambda sum, count and group by

Select sum(paidAmount), count(paidAmount), classificationName,
From tableA
Group by classificationName;

How can i do this in Java 8 using streams and collectors?

Java8:

lineItemList.stream()
            .collect(Collectors.groupingBy(Bucket::getBucketName,
                       Collectors.reducing(BigDecimal.ZERO,
                                           Bucket::getPaidAmount,
                                           BigDecimal::add)))

This gives me sum and group by. But how can I also get count on the group name ?

Expectation is :

100, 2, classname1 
50, 1, classname2
150, 3, classname3
like image 390
vkt Avatar asked Jan 22 '18 20:01

vkt


People also ask

How will you get the sum of all numbers present in a list using Java 8?

We generally iterate through the list when adding integers in a range, but java. util. stream. Stream has a sum() method that when used with filter() gives the required result easily.

How do I add two numbers in Java 8?

b) By Using the Sum() Method The sum() method is used to add numbers given in the arguments of the method. The sum() method is in the Integer class of Java which is in the util package. We will use the Integer. sum() function.

How do you sum a count in Java?

So you simply make this: sum=sum+num; for the cycle. For example sum is 0, then you add 5 and it becomes sum=0+5 , then you add 6 and it becomes sum = 5 + 6 and so on.


2 Answers

Using an extended version of the Statistics class of this answer,

class Statistics {
    int count;
    BigDecimal sum;

    Statistics(Bucket bucket) {
        count = 1;
        sum = bucket.getPaidAmount();
    }
    Statistics() {
        count = 0;
        sum = BigDecimal.ZERO;
    }

    void add(Bucket b) {
        count++;
        sum = sum.add(b.getPaidAmount());
    }

    Statistics merge(Statistics another) {
        count += another.count;
        sum = sum.add(another.sum);
        return this;
    }
}

you can use it in a Stream operation like

Map<String, Statistics> map = lineItemList.stream()
    .collect(Collectors.groupingBy(Bucket::getBucketName,
        Collector.of(Statistics::new, Statistics::add, Statistics::merge)));

this may have a small performance advantage, as it only creates one Statistics instance per group for a sequential evaluation. It even supports parallel evaluation, but you’d need a very large list with sufficiently large groups to get a benefit from parallel evaluation.

For a sequential evaluation, the operation is equivalent to

lineItemList.forEach(b ->
    map.computeIfAbsent(b.getBucketName(), x -> new Statistics()).add(b));

whereas merging partial results after a parallel evaluation works closer to the example already given in the linked answer, i.e.

secondMap.forEach((key, value) -> firstMap.merge(key, value, Statistics::merge));
like image 123
Holger Avatar answered Oct 17 '22 18:10

Holger


As you're using BigDecimal for the amounts (which is the correct approach, IMO), you can't make use of Collectors.summarizingDouble, which summarizes count, sum, average, min and max in one pass.

Alexis C. has already shown in his answer one way to do it with streams. Another way would be to write your own collector, as shown in Holger's answer.

Here I'll show another way. First let's create a container class with a helper method. Then, instead of using streams, I'll use common Map operations.

class Statistics {
    int count;
    BigDecimal sum;

    Statistics(Bucket bucket) {
        count = 1;
        sum = bucket.getPaidAmount();
    }

    Statistics merge(Statistics another) {
        count += another.count;
        sum = sum.add(another.sum);
        return this;
    }
}

Now, you can make the grouping as follows:

Map<String, Statistics> result = new HashMap<>();
lineItemList.forEach(b -> 
    result.merge(b.getBucketName(), new Statistics(b), Statistics::merge));

This works by using the Map.merge method, whose docs say:

If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function

like image 44
fps Avatar answered Oct 17 '22 18:10

fps