Java 8 lambda sum,count和group by

use*_*281 4 java java-8 java-stream

Select sum(paidAmount), count(paidAmount), classificationName,
From tableA
Group by classificationName;
Run Code Online (Sandbox Code Playgroud)

我如何使用流和收集器在Java 8中执行此操作?

Java8:

lineItemList.stream()
            .collect(Collectors.groupingBy(Bucket::getBucketName,
                       Collectors.reducing(BigDecimal.ZERO,
                                           Bucket::getPaidAmount,
                                           BigDecimal::add)))
Run Code Online (Sandbox Code Playgroud)

这给了我总和和分组.但是我怎么能依靠团体名称呢?

期望是:

100, 2, classname1 
50, 1, classname2
150, 3, classname3
Run Code Online (Sandbox Code Playgroud)

Fed*_*ner 5

当您使用BigDecimal金额(这是正确的方法,IMO)时,您无法使用Collectors.summarizingDouble,它总结了一次通过中的计数,总和,平均值,最小值和最大值.

亚历克西斯C.已经在他的回答中表明一种用流做的方法.另一种方法是编写自己的收藏家,如Holger的回答所示.

在这里,我将展示另一种方式.首先让我们用一个帮助器方法创建一个容器类.然后,我将使用常用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以进行如下分组:

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

这通过使用该Map.merge方法工作,其文档说:

如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联.否则,将相关值替换为给定重映射函数的结果


Hol*_*ger 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以在Stream操作中使用它

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

这可能具有很小的性能优势,因为它只Statistics为每个组创建一个实例以进行顺序评估.它甚至支持并行评估,但是您需要一个包含足够大的组的非常大的列表,以从并行评估中获益.

对于顺序评估,操作等同于

lineItemList.forEach(b ->
    map.computeIfAbsent(b.getBucketName(), x -> new Statistics()).add(b));
Run Code Online (Sandbox Code Playgroud)

而在并行评估之后合并部分结果更接近已经在链接答案中给出的示例,即

secondMap.forEach((key, value) -> firstMap.merge(key, value, Statistics::merge));
Run Code Online (Sandbox Code Playgroud)


Ale*_* C. 3

您可以减少对,其中键将保存总和,值将保存计数:

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()))));
Run Code Online (Sandbox Code Playgroud)

虽然Collectors.toMap看起来更干净:

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())));
Run Code Online (Sandbox Code Playgroud)