Java 8 Stream多条件过滤、按月份分组统计人数问题求助
问题描述
需要实现Java 8 Stream多条件过滤Person对象、按eventDate的月份分组并统计各类事件的人数,数据存储为Map<String(pId), List<Person>>。
相关类定义
Person类
public class Person{ private String id; private String name; private String surname; private Statement event; // 对应JOIN、EXIT private Object value; private LocalDate eventDate; // 需补充以下getter方法: // public Statement getEvent() // public LocalDate getEventDate() }
DTO类
public class DTO { private int month; private State info; // State为枚举类型,包含JOIN、EXIT private int totalEmployees; // 需补充构造方法: // public DTO(int month, State info, int totalEmployees) // 需补充getter方法:public int getMonth() }
GroupDto类
public class GroupDto { private int month; private State info; // 需补充构造方法: // public GroupDto(int month, State info) // 必须重写equals()和hashCode()方法,否则分组逻辑失效 }
示例数据
ID,Info,Date (所有值存储在Person对象的List中) per1, JOIN, 10-01-2022 per2, JOIN, 10-01-2022 per3, EXIT, 10-01-2022 per3, EXIT, 10-02-2022 per4, JOIN, 10-03-2022
预期结果
Month Info Total Number 1 JOIN 2 1 EXIT 1 2 EXIT 1 3 JOIN 1
现有错误代码片段
List<DTO> result = persons.values().stream() .flatMap(List::stream) .filter(person -> person.getInfo() == Value.ONBOARD || person.getInfo() == Value.EXIT) .collect(Collectors.groupingBy( p -> new GroupDto(p.getEventDate().getMonthValue(), p.getEvent()), Collectors.counting() )) .entrySet().stream() .map(e -> new DTO(p.getKey().get, p.getKey(), (int) (long) e.getValue())) // 错误行 .sorted(Comparator.comparing(MonthWiseDto::getMonth)) .toList();
修正方案及代码
核心错误点梳理
- 枚举/字段类型不匹配:原代码混用
Value、Statement、State枚举类型,需统一为State枚举(确保Person的event字段类型与DTO、GroupDto的info字段一致)。 - GroupDto缺失关键方法:
groupingBy依赖分组键的equals()和hashCode()判断分组,必须为GroupDto重写这两个方法。 - map转换逻辑错误:错误行中
p.getKey()是笔误,应使用e.getKey();且DTO构造参数传递错误,需从GroupDto中提取month和info。 - 类名引用错误:排序时误用
MonthWiseDto,实际应为DTO。 - Java版本适配:Java 8中无
toList()方法,需用collect(Collectors.toList())。
完整修正代码
先完善GroupDto类
import java.util.Objects; public class GroupDto { private int month; private State info; public GroupDto(int month, State info) { this.month = month; this.info = info; } // getter方法 public int getMonth() { return month; } public State getInfo() { return info; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GroupDto groupDto = (GroupDto) o; return month == groupDto.month && info == groupDto.info; } @Override public int hashCode() { return Objects.hash(month, info); } }
完善DTO类
public class DTO { private int month; private State info; private int totalEmployees; public DTO(int month, State info, int totalEmployees) { this.month = month; this.info = info; this.totalEmployees = totalEmployees; } public int getMonth() { return month; } // 可选:补充其他字段的getter方法用于结果输出 }
主逻辑代码
import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class StreamDemo { public static void main(String[] args) { Map<String, List<Person>> persons = // 你的数据源 List<DTO> result = persons.values().stream() .flatMap(List::stream) // 过滤JOIN和EXIT类型的事件 .filter(person -> State.JOIN.equals(person.getEvent()) || State.EXIT.equals(person.getEvent())) // 按月份+事件类型分组,统计人数 .collect(Collectors.groupingBy( p -> new GroupDto(p.getEventDate().getMonthValue(), p.getEvent()), Collectors.counting() )) .entrySet().stream() // 将分组结果转换为DTO .map(entry -> new DTO( entry.getKey().getMonth(), entry.getKey().getInfo(), Math.toIntExact(entry.getValue()) // 安全转换long为int,避免溢出 )) // 按月份排序 .sorted(Comparator.comparing(DTO::getMonth)) // Java 8收集结果的方式 .collect(Collectors.toList()); } } // 定义统一的State枚举 enum State { JOIN, EXIT }
补充说明
- 确保Person类的
event字段类型为State枚举,或在过滤时做类型兼容转换; Math.toIntExact()用于安全转换统计数(long类型)为int,避免强制转换可能的数值溢出问题;- 若使用Java 16及以上版本,可将最后一行的
collect(Collectors.toList())替换为toList()。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

