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

Java 17 Spring Webflux优化ArrayList单次流处理计算方案咨询

单次遍历完成多字段统计的高效实现方案

先修正基础问题

首先指出两处影响功能和性能的问题:

  1. 实体类字段类型不合理:numberOfDays、totalHours等数值型字段用String存储,每次计算都要解析字符串,既影响性能还可能抛出NumberFormatException,建议直接改为数值类型(比如Double或BigDecimal,根据业务精度需求选择):
@Column(name = "NUMBER_OF_DAYS")
private Double numberOfDays;

@Column(name = "TOTAL_HOURS")
private Double totalHours;

// 其他数值字段同理修改
  1. 仓库方法参数错误:实体类中date是LocalDate类型,但仓库方法的date参数是int,完全不匹配,会导致查询失败,修正为:
@Query("FROM EmployeeDO e WHERE e.deptId =:deptId and e.date =:date ORDER BY e.date ASC")
List<EmployeeDO> getEmployeeRec(@Param("deptId") int deptId,
                                @Param("date") LocalDate date);

单次遍历实现多统计的方案

我们可以自定义一个统计结果DTO,用来存储所有需要的总和、计数(平均值=总和/计数),然后通过单次遍历完成所有数据收集,有两种实现方式:

方式1:使用Stream.reduce(函数式风格)

先定义统计DTO:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class EmployeeStats {
    // 存储各字段总和
    private double totalDays;
    private double totalHours;
    private double totalManpower;
    private double totalWages;
    // 记录数据条数,用于计算平均值
    private long count;

    // 提供平均值计算方法
    public double getAvgDays() {
        return count == 0 ? 0 : totalDays / count;
    }

    public double getAvgHours() {
        return count == 0 ? 0 : totalHours / count;
    }

    public double getAvgWages() {
        return count == 0 ? 0 : totalWages / count;
    }
}

然后用reduce单次遍历集合:

var empList = employeeDao.getEmployeeRec(100, LocalDate.parse("2023-08-14"));

EmployeeStats stats = empList.stream()
    .reduce(
        // 初始化累加器
        EmployeeStats.builder()
            .totalDays(0)
            .totalHours(0)
            .totalManpower(0)
            .totalWages(0)
            .count(0)
            .build(),
        // 遍历每个元素,更新累加器
        (acc, emp) -> {
            acc.setTotalDays(acc.getTotalDays() + emp.getNumberOfDays());
            acc.setTotalHours(acc.getTotalHours() + emp.getTotalHours());
            acc.setTotalManpower(acc.getTotalManpower() + emp.getTotalManpower());
            acc.setTotalWages(acc.getTotalWages() + emp.getTotalWages());
            acc.setCount(acc.getCount() + 1);
            return acc;
        },
        // 并行流场景下合并两个累加器(串行流不会执行此逻辑)
        (stats1, stats2) -> {
            stats1.setTotalDays(stats1.getTotalDays() + stats2.getTotalDays());
            stats1.setTotalHours(stats1.getTotalHours() + stats2.getTotalHours());
            stats1.setTotalManpower(stats1.getTotalManpower() + stats2.getTotalManpower());
            stats1.setTotalWages(stats1.getTotalWages() + stats2.getTotalWages());
            stats1.setCount(stats1.getCount() + stats2.getCount());
            return stats1;
        }
    );

// 获取最终统计结果
double totalDays = stats.getTotalDays();
double avgHours = stats.getAvgHours();
double totalManpower = stats.getTotalManpower();
double avgWages = stats.getAvgWages();

方式2:传统for循环(更直观,性能略优)

如果觉得Stream的写法不够直观,直接用for循环遍历,性能和reduce相当,甚至在小数据量下更高效:

var empList = employeeDao.getEmployeeRec(100, LocalDate.parse("2023-08-14"));

EmployeeStats stats = new EmployeeStats(0, 0, 0, 0, 0);
for (EmployeeDO emp : empList) {
    stats.setTotalDays(stats.getTotalDays() + emp.getNumberOfDays());
    stats.setTotalHours(stats.getTotalHours() + emp.getTotalHours());
    stats.setTotalManpower(stats.getTotalManpower() + emp.getTotalManpower());
    stats.setTotalWages(stats.getTotalWages() + emp.getTotalWages());
    stats.setCount(stats.getCount() + 1);
}

// 同样通过stats的get方法获取总和、平均值

方案优势

  • 仅遍历集合一次,避免了多次遍历带来的性能开销,数据量越大优势越明显
  • 所有统计逻辑集中管理,便于维护
  • 修正了原代码中的类型错误,避免潜在的运行时异常

内容的提问来源于stack exchange,提问作者Senthilkumar P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 13:07:49