使用Collectors.groupingBy按businessType字段分组费用对象,支持基础分组、求和统计、计数及自定义聚合;需处理null值,推荐用Objects.toString兜底或提前过滤。

用 Collectors.groupingBy 按业务类型汇总费用支出,核心是把费用对象按 businessType 字段分组,再对每组做聚合(如求和、计数、收集明细等)。关键在于分组键的提取方式和下游收集器的选择。
明确费用对象结构与分组字段
确保费用实体类(如 Expense)中存在可作为业务类型的字段,例如:
public class Expense {
private String businessType; // 如 "研发", "市场", "行政"
private BigDecimal amount;
private String description;
// getter/setter...
}
分组时直接引用该字段,注意判空处理避免 NullPointerException:
- 使用
Objects.toString(expense.getBusinessType(), "未知")统一兜底 - 或提前过滤掉
businessType == null的记录
基础分组:按业务类型归集费用列表
最常用场景是查看每个业务类型下有哪些具体支出:
Map<string list>> groupedByType = expenses.stream()
.collect(Collectors.groupingBy(
expense -> Objects.toString(expense.getBusinessType(), "未知")
));</string>
结果为 Map>,可直接遍历打印明细或转为报表数据。
带聚合的分组:统计每类总金额或笔数
在 groupingBy 第二个参数传入下游收集器,实现边分组边计算:
- 汇总每类总支出:
Collectors.summingBigDecimal(Expense::getAmount) - 统计每类支出笔数:
Collectors.counting() - 同时获取总金额和明细(用
Collectors.collectingAndThen或自定义容器)
Map<string bigdecimal> totalByType = expenses.stream()
.collect(Collectors.groupingBy(
expense -> Objects.toString(expense.getBusinessType(), "未知"),
Collectors.summingBigDecimal(Expense::getAmount)
));</string>
进阶组合:分组后保留明细并附加统计值
若需既看到每类所有费用项,又附带小计、平均值等,可用 Collectors.collectingAndThen 包装一个自定义对象:
Map<string typesummary> summaryMap = expenses.stream()
.collect(Collectors.groupingBy(
expense -> Objects.toString(expense.getBusinessType(), "未知"),
Collectors.collectingAndThen(
Collectors.toList(),
list -> new TypeSummary(
list,
list.stream().map(Expense::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add),
(double) list.size()
)
)
));</string>
其中 TypeSummary 是你定义的聚合结果类,封装明细列表、总额、数量等字段,便于前端展示或导出。










