Java groupingBy:对多个字段求和

Use*_*108 6 java java-8 java-stream collectors

这个问题是帖子的扩展:Java 8 groupingby with Return multiple field

对于同样的问题,你如何返回一个列表Customer?例如,它应该返回:

Customer("A",4500,6500)
Customer("B",3000,3500)
Customer("C",4000,4500)
Run Code Online (Sandbox Code Playgroud)

Pan*_*hal 5

使用以下代码:

Map<String, Customer> retObj =
    listCust.stream()
        .collect(Collectors.groupingBy(Customer::getName,
                    Collector.of(
                        Customer::new,
                        (c1, c2) -> {
                            c1.setName(c2.getName());
                            c1.setTotal(c1.getTotal() + c2.getTotal());
                            c1.setBalance(c1.getBalance() + c2.getBalance());
                        },
                        (c3, c4) -> {
                            c3.setTotal(c3.getTotal() + c4.getTotal());
                            c3.setBalance(c3.getBalance() + c4.getBalance());
                            return c3;
                        })));

System.out.println(retObj);
System.out.println(retObj.values());       //If you want only the list of all Customers
Run Code Online (Sandbox Code Playgroud)

输出:

{
 A=Customer [name=A, total=4500.0, balance=6500.0], 
 B=Customer [name=B, total=3000.0, balance=3500.0], 
 C=Customer [name=C, total=4000.0, balance=4500.0]
}
Run Code Online (Sandbox Code Playgroud)


Ous*_* D. 4

Map<String, Customer>如果您想要 a作为结果集 +1,@Pankaj Singhal 的帖子是正确的想法。但是,我会将合并逻辑提取到其自己的函数中,例如在Customer类中您将拥有这样的函数:

public static Customer merge(Customer first, Customer second) {
        first.setTotal(first.getTotal() + second.getTotal());
        first.setBalance(first.getBalance() + second.getBalance());
        return first;
}
Run Code Online (Sandbox Code Playgroud)

那么流查询将变为:

Map<String, Customer> retObj = 
           listCust.stream()
                   .collect(Collectors.toMap(Customer::getName, Function.identity(), Customer::merge)); 
Run Code Online (Sandbox Code Playgroud)
  • listCust.stream()创建一个对象,即Stream<Customer>
  • collect使用提供的 对此流的元素执行可变归约操作Collector
  • 的结果toMap是提供的收集器,该toMap方法提取键 Customer::getName和值,如果映射的键包含重复项,则使用Function.identity()合并函数来解决冲突。Customer::merge

我认为将合并逻辑提取到其自己的函数中具有三个好处:

  • 代码更加紧凑。
  • 代码更具可读性。
  • 合并的复杂性与流隔离。

但是,如果您的意图是检索Collection<Customer>

Collection<Customer> result = listCust.stream()
                .collect(Collectors.toMap(Customer::getName,
                        Function.identity(),
                        Customer::merge))
                .values();
Run Code Online (Sandbox Code Playgroud)

或者List<Customer>作为结果集,您所要做的就是调用values()并将结果传递给ArrayList构造函数:

List<Customer> result = new ArrayList<>(listCust.stream()
                .collect(Collectors.toMap(Customer::getName,
                        Function.identity(),
                        Customer::merge))
                .values());
Run Code Online (Sandbox Code Playgroud)

更新:

如果您不想改变源中的对象,则只需修改该merge函数即可,如下所示:

public static Customer merge(Customer first, Customer second) {
        Customer customer = new Customer(first.getName(), first.getTotal(), first.getBalance());
        customer.setTotal(customer.getTotal() + second.getTotal());
        customer.setBalance(customer.getBalance() + second.getBalance());
        return customer;
}
Run Code Online (Sandbox Code Playgroud)

其他一切都保持原样。