如何在Java中基于日期对含分类日期的条目列表按月份分组并统计各分类总金额?
按月份分组并统计分类总金额的实现方案
嘿,我来帮你搞定这个按月份分组统计分类金额的需求!你的代码思路方向是对的,但有几个小问题需要调整,我一步步给你讲清楚:
先说说现有代码的问题
- 收集器嵌套错误:你在
groupingBy之后直接链式调用collect是不对的,groupingBy本身就需要指定下游收集器来处理分组内的数据,不能单独再套一层collect - 日期与字段匹配问题:输入JSON的日期格式是
dd-MM-yy,得先正确解析成LocalDate;而且JSON里的字段是Category、Amount,你的Data类用的是getName()、getAmount(),字段名得对齐才能正确映射 - 缺少金额聚合逻辑:你还没实现同一分类下金额求和的逻辑,这才是需求的核心部分
修正后的完整实现
1. 先搞定Data实体类
首先得让实体类能正确解析JSON里的日期和金额,字段名也要对应上:
import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class Data { // 匹配输入JSON的日期格式:dd-MM-yy private static final DateTimeFormatter DATE_PARSER = DateTimeFormatter.ofPattern("dd-MM-yy"); private LocalDate date; private String category; private int amount; // 构造方法,把JSON里的字符串转成对应类型 public Data(String dateStr, String category, String amountStr) { // 两位年份默认解析为20xx(比如19就是2019) this.date = LocalDate.parse(dateStr, DATE_PARSER); this.category = category; this.amount = Integer.parseInt(amountStr); } // Getter方法,供Stream操作使用 public LocalDate getDate() { return date; } public String getCategory() { return category; } public int getAmount() { return amount; } }
2. 核心分组统计逻辑
这里我们用嵌套的groupingBy:先按当月第一天把所有条目分到不同月份组,然后在每个月份组里,再按分类聚合总金额。最后把结果格式化成你想要的输出样式:
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; public class MonthlyCategoryStats { // 用来把月份格式化成Sep-19这种样式 private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("MMM-yy"); private static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>(); // 初始化时间调整器,和你原来的逻辑一致 static { ADJUSTERS.put("day", TemporalAdjusters.ofDateAdjuster(d -> d)); ADJUSTERS.put("week", TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); ADJUSTERS.put("month", TemporalAdjusters.firstDayOfMonth()); ADJUSTERS.put("year", TemporalAdjusters.firstDayOfYear()); } public static void main(String[] args) { // 模拟从JSON解析得到的Data列表(实际项目里可以用Jackson/Gson来解析JSON) List<Data> dataList = Arrays.asList( new Data("12-09-19", "Bills", "100"), new Data("11-09-19", "Grocery", "1010"), new Data("21-08-19", "Other Household Bills", "1320"), new Data("11-08-19", "Service", "2100"), new Data("11-09-19", "Food & Dining Out", "1010"), new Data("21-08-19", "Service", "100"), new Data("18-09-19", "Bills", "100") ); // 核心逻辑:按月份分组,每个分组内按分类求和 Map<LocalDate, Map<String, Integer>> monthlyStats = dataList.stream() .collect(Collectors.groupingBy( // 把每个日期转成当月第一天,实现按月份分组 item -> item.getDate().with(ADJUSTERS.get("month")), // 下游收集器:每个月内按分类分组,然后求和金额 Collectors.groupingBy(Data::getCategory, Collectors.summingInt(Data::getAmount)) )); // 把统计结果格式化成你要的输出样式 monthlyStats.entrySet().stream() // 按月份倒序排列,让最近的月份先显示 .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())) .forEach(entry -> { String monthLabel = entry.getKey().format(MONTH_FORMATTER); String categoryDetails = entry.getValue().entrySet().stream() .map(categoryEntry -> categoryEntry.getKey() + ":" + categoryEntry.getValue()) .collect(Collectors.joining(", ")); System.out.println(monthLabel + " " + categoryDetails); }); } }
3. 运行结果
运行这段代码后,会输出你预期的结果:
Sep-19 Grocery:1010, Food & Dining Out:1010, Bills:200 Aug-19 Other Household Bills:1320, Service:2200
关键细节解释
- 月份分组的实现:用
TemporalAdjusters.firstDayOfMonth()把每个日期转换为当月的第一天,这样同一个月的所有条目都会被映射到同一个LocalDate键上,实现按月份分组 - 分类金额求和:嵌套使用
Collectors.groupingBy和Collectors.summingInt,先按月份分组,再在每个月的分组内,按分类名称聚合该分类的总金额 - 输出格式化:用
DateTimeFormatter.ofPattern("MMM-yy")把月份对象转换成Sep-19这种易读的字符串,再把分类和对应的总金额拼接成你想要的格式
内容的提问来源于stack exchange,提问作者Android God
相关产品推荐
相关产品推荐

