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

如何用Java Stream按personalId汇总薪资并转换为SalaryDto?

问题描述

现有一个Map<String, List<Employee>>集合,键为Employee的id,值是对应id的Employee实例列表,示例数据如下:

Id, Name , Surname, Salary
personalId1, "Name 1","Surname 1", 100
personalId2, "Name 2","Surname 2", 100
personalId3, "Name 3","Surname 3", 100
personalId1, "Name 1","Surname 1", 100
personalId2, "Name 2","Surname 2", 100
personalId2, "Name 2","Surname 2", 100
...

需要按personalId分组,汇总每个id对应的薪资总和,最终得到如下格式的结果:

personalId1, "Name 1","Surname 1", 200
personalId2, "Name 2","Surname 2", 300
personalId3, "Name 3","Surname 3", 100

已定义SalaryDto类:

public class SalaryDto {
    private String id;
    private String name;
    private String surname;
    private BigDecimal totalSalary;

    // 需补充构造方法、getter/setter,示例构造方法:
    // public SalaryDto(String id, String name, String surname, BigDecimal totalSalary) {}
    // 用于累加薪资的方法:
    public void addSalary(BigDecimal salary) {
        this.totalSalary = this.totalSalary.add(salary);
    }
}

尝试用Java Stream实现时,在flatMap后遇到问题,希望用reduce实现按personalId汇总,现有代码片段:

List<SalaryDto> totalSalary = employees.values().stream()
    .flatMap(List::stream)
    .map(e-> new SalaryDto(...))
    .collect(groupingBy(Personal::getID, Collectors.toList()));

请问该如何正确实现?

解决方案

方式一:分组+reducing组合实现

直接通过Collectors.groupingBy搭配Collectors.reducing,一次性完成分组与薪资汇总,代码更简洁:

import java.util.stream.Collectors;
import java.util.List;

List<SalaryDto> totalSalaryList = employees.values().stream()
        .flatMap(List::stream)
        // 按personalId分组,同时对每组的Employee进行reduce合并
        .collect(Collectors.groupingBy(
                Employee::getId,
                Collectors.reducing(
                        null,
                        emp -> new SalaryDto(emp.getId(), emp.getName(), emp.getSurname(), emp.getSalary()),
                        (dto1, dto2) -> {
                            if (dto1 == null) return dto2;
                            dto1.addSalary(dto2.getTotalSalary());
                            return dto1;
                        }
                )
        ))
        .values()
        .stream()
        .collect(Collectors.toList());

方式二:先分组再显式reduce

如果更倾向于单独使用reduce操作,可以先按id分组,再对每组的Employee列表单独执行reduce:

import java.util.stream.Collectors;
import java.util.List;
import java.util.Map;

// 先按id分组
Map<String, List<Employee>> groupedEmployees = employees.values().stream()
        .flatMap(List::stream)
        .collect(Collectors.groupingBy(Employee::getId));

// 对每个分组执行reduce汇总薪资
List<SalaryDto> totalSalaryList = groupedEmployees.entrySet().stream()
        .map(entry -> {
            String id = entry.getKey();
            List<Employee> empList = entry.getValue();
            // 初始化SalaryDto,用第一个员工的姓名,薪资初始为0
            return empList.stream()
                    .reduce(
                            new SalaryDto(id, empList.get(0).getName(), empList.get(0).getSurname(), BigDecimal.ZERO),
                            (dto, emp) -> {
                                dto.addSalary(emp.getSalary());
                                return dto;
                            },
                            (dto1, dto2) -> {
                                dto1.addSalary(dto2.getTotalSalary());
                                return dto1;
                            }
                    );
        })
        .collect(Collectors.toList());

关键注意点

  1. 确保SalaryDto有对应的构造方法和addSalary方法,或者直接通过setter更新薪资总和;
  2. 若薪资类型为BigDecimal,必须使用BigDecimal.add()进行累加,避免浮点数精度丢失;
  3. 假设同一personalId对应的员工姓名、姓氏一致,因此汇总时直接取组内第一个员工的姓名即可。

内容的提问来源于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 04:15:42