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 the amount and group. But how can I also count on the name of the group?

Expectation:

100, 2, classname1 
50, 1, classname2
150, 3, classname3
+4
source share
3 answers

Since you use BigDecimalfor sums (this is the correct approach, IMO), you cannot use Collectors.summarizingDoubleone that sums up the amount, amount, average, min and maximum in one pass.

Alexis C. already showed in his answer one way to do this with threads. Another way is to write your own collector, as shown in Holger's answer .

. . Map.

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;
    }
}

:

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

Map.merge, :

, .

+5

Statistics ,

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;
    }
}

Stream,

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

, Statistics . , , .

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

, , ..

secondMap.forEach((key, value) -> firstMap.merge(key, value, Statistics::merge));
+5

You could reduce the pairs where the keys would hold the amount, and the values ​​would contain a counter:

Map<String, SimpleEntry<BigDecimal, Long>> map = 
    lineItemList.stream()
                .collect(groupingBy(Bucket::getBucketName,
                         reducing(new SimpleEntry<>(BigDecimal.ZERO, 0L), 
                                  b -> new SimpleEntry<>(b.getPaidAmount(), 1L), 
                                  (v1, v2) -> new SimpleEntry<>(v1.getKey().add(v2.getKey()), v1.getValue() + v2.getValue()))));

although Collectors.toMapit looks cleaner:

Map<String, SimpleEntry<BigDecimal, Long>> map = 
    lineItemList.stream()
                .collect(toMap(Bucket::getBucketName,
                               b -> new SimpleEntry<>(b.getPaidAmount(), 1L),
                               (v1, v2) -> new SimpleEntry<>(v1.getKey().add(v2.getKey()), v1.getValue() + v2.getValue())));
+3
source

Source: https://habr.com/ru/post/1692552/


All Articles