使用 Java Stream 进行 Group By 操作并计算字段之和

在这篇文章中,我们将一起学习如何使用 Java Stream API 来对一个 List 进行分组并计算某个字段的总和。对于 Java 开发者来说,掌握流操作是一项非常重要的技能。我们将通过以下步骤来完成这个任务:

步骤 描述
1 创建一个示例对象类(如 Order)。
2 创建一个示例数据列表。
3 使用 Stream 进行分组。
4 计算每个组的某个字段总和。
5 打印结果。

接下来,我们将详细讨论每一步的具体实现。

一、创建一个示例对象类

首先,我们需要创建一个对象类。在这个例子中,我们定义一个 Order 类,包含几个基本字段。

public class Order {
    private String category; // 订单类别
    private double amount;    // 订单金额
    
    // 构造函数
    public Order(String category, double amount) {
        this.category = category;
        this.amount = amount;
    }
    
    // Getter 方法
    public String getCategory() {
        return category;
    }
    
    public double getAmount() {
        return amount;
    }
}

解释

  • Order 类表示一个订单对象,包含 categoryamount 两个字段。
  • 构造函数用于初始化订单的类别和金额。
  • Getter 方法用于获取字段值。

二、创建一个示例数据列表

接下来,我们需要创建一个订单列表以供后续操作。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("Electronics", 100.0));
        orders.add(new Order("Clothing", 50.0));
        orders.add(new Order("Electronics", 150.0));
        orders.add(new Order("Clothing", 75.0));
        orders.add(new Order("Books", 30.0));
        
        // 在此继续实现 Grouping 和 Summation
    }
}

解释

  • 我们创建了一个 ArrayList 实例 orders,并向其中添加了一些 Order 对象。

三、使用 Stream 进行分组

现在,我们可以使用 Java 8 的 Stream API 进行分组操作了。我们将使用 Collectors.groupingBy 方法来对订单按类别进行分组。

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

// 省略上面的代码

Map<String, List<Order>> groupedOrders = orders.stream()
    .collect(Collectors.groupingBy(Order::getCategory));

解释

  • orders.stream() 表示将订单列表转化为流。
  • Collectors.groupingBy(Order::getCategory) 表示根据订单的 category 字段进行分组,结果是一个以类别为键、订单列表为值的 Map。

四、计算每个组的某个字段总和

接下来,我们需要在分组数据上计算每个组的 amount 字段总和。

Map<String, Double> totalAmounts = groupedOrders.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey, // 取 Map 的键,即类别
        entry -> entry.getValue().stream()
            .mapToDouble(Order::getAmount) // 提取金额字段
            .sum() // 计算金额总和
    ));

解释

  • groupedOrders.entrySet().stream() 将分组后的 Map 转换为流。
  • Collectors.toMap(...) 用于生成一个新的 Map,其中包含类别及其对应的金额总和。
  • entry -> entry.getValue().stream().mapToDouble(Order::getAmount).sum() 表达式用于提取每个类别中订单的金额并计算总和。

五、打印结果

最后,我们将结果打印出来,以验证我们的计算是否正确。

totalAmounts.forEach((category, total) -> {
    System.out.println("Category: " + category + ", Total Amount: " + total);
});

解释

  • totalAmounts.forEach(...) 用于遍历最终的 Map,并打印每个类别及其对应的金额总和。

总结

通过这篇文章,我们展示了如何使用 Java Stream API 实现对数据的分组和求和。完整示例代码如下所示:

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

public class Main {
    
    public static void main(String[] args) {
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("Electronics", 100.0));
        orders.add(new Order("Clothing", 50.0));
        orders.add(new Order("Electronics", 150.0));
        orders.add(new Order("Clothing", 75.0));
        orders.add(new Order("Books", 30.0));
        
        Map<String, List<Order>> groupedOrders = orders.stream()
            .collect(Collectors.groupingBy(Order::getCategory));

        Map<String, Double> totalAmounts = groupedOrders.entrySet().stream()
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> entry.getValue().stream()
                    .mapToDouble(Order::getAmount)
                    .sum()
            ));

        totalAmounts.forEach((category, total) -> {
            System.out.println("Category: " + category + ", Total Amount: " + total);
        });
    }
}

// Order 类的实现略
sequenceDiagram
    participant User
    participant Java_Program
    User->>Java_Program: 创建订单列表
    Java_Program->>Java_Program: 使用 Stream 分组
    Java_Program->>Java_Program: 计算每组总和
    Java_Program->>User: 打印结果

通过以上步骤与代码示例,相信你已经掌握了如何使用 Java Stream 进行分组和求和的基本操作。希望这篇文章能够帮助你快速入门并掌握这一实用技巧!