首页 > 解决方案 > 按字段对对象列表进行分组并在其中一个字段中应用二进制函数以使用流 java 8 在一行代码中按总和减少它

问题描述

举个例子,我有一个交易列表,其中包含 txnId、paymentCode、金额。输出应该是每个付款代码明智地收集了多少金额。

我尝试根据付款代码作为键和付款代码的金额列表对其进行分组,但我无法减少它。

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Payment {

    Integer txnId;
    String paymentCode;
    Integer amount;

    Payment(Integer txnId, String paymentCode, Integer amount) {
        this.txnId = txnId;
        this.paymentCode = paymentCode;
        this.amount = amount;
    }

    public String getPaymentCode() {
        return this.paymentCode;
    }

    public Integer getAmount() {
        return this.amount;
    }

    @Override
    public String toString() {
        return "Payment [txnId=" + txnId + ", paymentCode=" + paymentCode + ", amount=" + amount + "]";
    }

}

public class ListGroupingSum {

    public static void main(String[] args) {

        List<Payment> payments = new ArrayList<>();
        payments.add(new Payment(100, "GPAY", 100));
        payments.add(new Payment(101, "CC", 200));
        payments.add(new Payment(102, "PAYTM", 300));
        payments.add(new Payment(103, "GPAY", 400));
        payments.add(new Payment(104, "CC", 500));

        // @formatter:off
        Map<String, List<Integer>> paymentListByCodeAndAmount = payments.stream()
                .collect(Collectors.groupingBy(Payment::getPaymentCode, Collectors.mapping(Payment::getAmount, Collectors.toList())));
        System.out.println(paymentListByCodeAndAmount);
        
        //System.out.println(paymentListByCodeAndAmount.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
        // @formatter:on
    }
}

这给出了输出

{CC=[200, 500], GPAY=[100, 400], PAYTM=[300]} 

但是,期望是

{CC=700, GPAY=500, PAYTM=300} 

标签: javacollectionsjava-8java-stream

解决方案


Map<String, Integer> paymentListByCodeAndAmount = payments.stream()
  .collect(groupingBy(Payment::getPaymentCode, summingInt(Payment::getAmount)));

(完全符合您的预期输出)

在此处输入图像描述

真的,summingInt是一个收集器groupingBy将放置每个组,您可以“汇总整数”或任何您想要的。

例如,如果组数据不是直接整数,则可以映射:

Map<String, Integer> paymentListByCodeAndAmount = payments.stream()
        .collect(groupingBy(Payment::getPaymentCode, 
                mapping(p -> p.amount * p.txnId, 
                        summingInt(x -> x))));

读作“将付款作为流,按付款代码分组,取金额和 txnId 的乘积,然后给我总和”

但是,当然,存在数以百万计的组合,而不是简单地总结你可以总结 (min, max, avg, ...) 并用这个做一些事情:

Map<String, Double> paymentListByCodeAndAmount = payments.stream()
        .collect(groupingBy(Payment::getPaymentCode,
                collectingAndThen(summarizingInt(p -> p.amount * p.txnId),
                        IntSummaryStatistics::getAverage)));

读作“将付款作为流,按付款代码分组,汇总金额和 txnId 的乘积,然后给我平均值”


推荐阅读