You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();

修正方案及代码

核心错误点梳理

  1. 枚举/字段类型不匹配:原代码混用Value、Statement、State枚举类型,需统一为State枚举(确保Person的event字段类型与DTO、GroupDto的info字段一致)。
  2. GroupDto缺失关键方法:groupingBy依赖分组键的equals()和hashCode()判断分组,必须为GroupDto重写这两个方法。
  3. map转换逻辑错误:错误行中p.getKey()是笔误,应使用e.getKey();且DTO构造参数传递错误,需从GroupDto中提取month和info。
  4. 类名引用错误:排序时误用MonthWiseDto,实际应为DTO。
  5. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 06:05:21