Java 6:基于POJO按服务/库名称分组统计状态数量方案咨询
解决方案
第一步:修正List的转换代码
你当前的循环存在严重问题:只创建了一个CampRepost对象,每次循环都覆盖它的属性,最终campRepostList里所有元素都是同一个对象的引用,数据全部被最后一次循环覆盖。必须把对象创建放到循环内部:
List<CampRepost> campRepostList = new ArrayList<>(); for (String line : values2) { // 先去除首尾空格,避免分割后出现空字符串或带空格的字段 String trimmedLine = line.trim(); String[] arrSplit = trimmedLine.split(","); CampRepost cmpRepost = new CampRepost(); // 每次循环创建新对象 cmpRepost.setServiceName(arrSplit[0].trim()); cmpRepost.setLibName(arrSplit[1].trim()); cmpRepost.setVersion(arrSplit[2].trim()); // 存储原始状态字符串的byte数组,后续统计时转成String处理 cmpRepost.setStatus(arrSplit[3].trim().getBytes()); campRepostList.add(cmpRepost); }
第二步:定义状态归一化规则
你的数据里状态存在格式不一致(比如OverDue和Overdue、Due in <= 90和Due in <=90),需要先统一格式,避免统计错误。可以定义一个工具方法来归一化状态:
private static String normalizeStatus(byte[] statusBytes) { String status = new String(statusBytes).trim().toLowerCase(); if (status.contains("overdue")) { return "Overdue"; } else if (status.contains("<= 90") || status.contains("<=90")) { return "Due in <= 90"; } else if (status.contains(">90")) { return "Due in >90"; } return status; // 处理未知状态 }
第三步:实现分组统计(透视表逻辑)
可以用Java Stream API结合分组收集器来实现,核心思路是:
- 按指定字段(
libName或serviceName)分组 - 对每组内的元素统计各状态的数量
方式1:自定义统计DTO(推荐)
先创建一个存储统计结果的类,让输出更清晰:
class StatusStatistics { private int dueLessEqual90 = 0; private int overdue = 0; private int dueGreater90 = 0; // 累加统计方法 public void countStatus(String normalizedStatus) { switch (normalizedStatus) { case "Due in <= 90": dueLessEqual90++; break; case "Overdue": overdue++; break; case "Due in >90": dueGreater90++; break; } } // Getter方法,用于输出 public int getDueLessEqual90() { return dueLessEqual90; } public int getOverdue() { return overdue; } public int getDueGreater90() { return dueGreater90; } }
按libName分组统计
// 按libName分组,每组统计状态数量 Map<String, StatusStatistics> libStatsMap = campRepostList.stream() .collect(Collectors.groupingBy( CampRepost::getLibName, Collectors.collectingAndThen( Collectors.toList(), list -> { StatusStatistics stats = new StatusStatistics(); list.forEach(camp -> { String status = normalizeStatus(camp.getStatus()); stats.countStatus(status); }); return stats; } ) )); // 输出报表 System.out.println("按libName分组统计:"); libStatsMap.forEach((libName, stats) -> { System.out.printf("libName: %s, Due in <= 90: %d, Overdue: %d, Due in >90: %d%n", libName, stats.getDueLessEqual90(), stats.getOverdue(), stats.getDueGreater90()); });
按serviceName分组统计
只需要修改分组的键为CampRepost::getServiceName即可:
Map<String, StatusStatistics> serviceStatsMap = campRepostList.stream() .collect(Collectors.groupingBy( CampRepost::getServiceName, Collectors.collectingAndThen( Collectors.toList(), list -> { StatusStatistics stats = new StatusStatistics(); list.forEach(camp -> { String status = normalizeStatus(camp.getStatus()); stats.countStatus(status); }); return stats; } ) )); // 输出报表 System.out.println("\n按serviceName分组统计:"); serviceStatsMap.forEach((serviceName, stats) -> { System.out.printf("serviceName: %s, Due in <= 90: %d, Overdue: %d, Due in >90: %d%n", serviceName, stats.getDueLessEqual90(), stats.getOverdue(), stats.getDueGreater90()); });
方式2:用嵌套Map直接统计
如果不想创建DTO,可以用Map<String, Map<String, Integer>>来存储,外层键是分组字段,内层键是状态,值是数量:
// 按libName分组统计 Map<String, Map<String, Integer>> libStatsMap = campRepostList.stream() .collect(Collectors.groupingBy( CampRepost::getLibName, Collectors.toMap( camp -> normalizeStatus(camp.getStatus()), v -> 1, Integer::sum // 重复状态时累加数量 ) )); // 输出时补全缺失的状态(数量为0) System.out.println("按libName分组统计:"); libStatsMap.forEach((libName, statusMap) -> { int due90 = statusMap.getOrDefault("Due in <= 90", 0); int overdue = statusMap.getOrDefault("Overdue", 0); int dueGt90 = statusMap.getOrDefault("Due in >90", 0); System.out.printf("libName: %s, Due in <= 90: %d, Overdue: %d, Due in >90: %d%n", libName, due90, overdue, dueGt90); });
关键注意事项
- 状态归一化:必须处理数据中状态的大小写、空格差异,否则会出现统计遗漏
- 对象创建位置:循环内必须每次创建新的
CampRepost对象,否则列表数据会被覆盖 - 空值/异常处理:如果
values2中存在格式错误的行(比如分割后字段数不足4),需要添加判断逻辑避免数组越界
内容的提问来源于stack exchange,提问作者Selenium New bie
相关产品推荐
相关产品推荐

