首页 > Java > java教程 > 正文

Java中计算列表数据中按条件分组的连续失败时长

霞舞
发布: 2025-12-12 21:18:01
原创
186人浏览过

Java中计算列表数据中按条件分组的连续失败时长

本教程详细介绍了如何在java中处理包含时间序列和状态信息的列表数据,以计算每个实体(如用户)的累积失败时长。通过将数据按实体分组,并利用java stream api或第三方seq库进行排序和有状态遍历,精确地统计从“失败”状态开始到下一个“成功”状态结束的持续时间。文章提供了具体的代码示例,并讨论了实现细节及注意事项。

在现代数据处理中,我们经常需要从一系列事件记录中提取有意义的聚合信息。一个常见场景是,给定一个包含实体名称、事件日期和状态(例如“成功”或“失败”)的列表,我们需要计算每个实体在特定条件下的累计时长。本教程将以计算“连续失败时长”为例,详细讲解如何使用Java的Stream API以及第三方库Seq来高效解决此类问题。

问题描述

假设我们有一组按时间顺序排列的事件记录,每条记录包含一个实体名称(name)、事件发生的年份(date)和事件状态(status,可以是"success"或"fail")。我们需要为每个实体计算其总的“失败时长”。失败时长定义为:从一个“失败”状态开始,到紧随其后的第一个“成功”状态结束的时间跨度。如果在一个失败周期中出现多个连续的失败事件,它们将被视为同一失败周期的延续。

示例数据:

[
   {"name":"john", "date":2015, "status":"success"},
   {"name":"john", "date":2013, "status":"fail"},
   {"name":"chris", "date":2013, "status":"success"},
   {"name":"john", "date":2012, "status":"fail"},
   {"name":"john", "date":2009, "status":"success"},
   {"name":"chris", "date":2007, "status":"fail"},
   {"name":"john", "date":2005, "status":"fail"},
]
登录后复制

根据上述定义,对于john:

立即学习Java免费学习笔记(深入)”;

文心智能体平台
文心智能体平台

百度推出的基于文心大模型的Agent智能体平台,已上架2000+AI智能体

文心智能体平台 393
查看详情 文心智能体平台
  • 2005年失败,下一个成功是2009年,失败时长为 2009 - 2005 = 4 年。
  • 2012年失败,2013年再次失败(视为同一失败周期),下一个成功是2015年,失败时长为 2015 - 2012 = 3 年。
  • john的总失败时长为 4 + 3 = 7 年。

对于chris:

  • 2007年失败,下一个成功是2013年,失败时长为 2013 - 2007 = 6 年。
  • chris的总失败时长为 6 年。

数据模型定义

为了更好地组织和处理数据,我们首先定义一个Java类来表示每条记录,而不是直接使用HashMap。这提供了更好的类型安全性和代码可读性

public class Record {
    public String name;
    public Integer date; // 使用Integer表示年份
    public String status;

    public Record(String name, Integer date, String status) {
        this.name = name;
        this.date = date;
        this.status = status;
    }

    @Override
    public String toString() {
        return "Record{" +
               "name='" + name + '\'' +
               ", date=" + date +
               ", status='" + status + '\'' +
               '}';
    }
}
登录后复制

核心计算逻辑

解决此类问题的关键在于对数据进行分组和按时间排序,并在遍历过程中维护一个状态。具体步骤如下:

  1. 按实体名称分组:首先,将所有记录根据其name字段进行分组。这样,我们可以独立地处理每个实体的事件序列。
  2. 组内按日期排序:对于每个实体组,必须将其内部的记录按照date字段升序排序。这是确保计算逻辑正确性的前提,因为失败时长的计算依赖于事件的先后顺序。
  3. 有状态遍历计算
    • 在遍历每个实体排序后的记录时,我们需要维护一个变量来记录当前是否处于一个失败周期中,以及该失败周期的起始日期。我们可以使用一个Integer类型的变量lastFailDate,初始值为null。
    • 当遇到一条状态为"fail"的记录时:
      • 如果lastFailDate为null,表示这是一个新的失败周期的开始,我们将当前记录的date赋值给lastFailDate。
      • 如果lastFailDate不为null,表示失败周期正在持续,我们不需要更新lastFailDate(因为我们只关心失败的起始点)。
    • 当遇到一条状态为"success"的记录时:
      • 如果lastFailDate不为null,表示当前有一个未结束的失败周期。此时,计算当前成功记录的date与lastFailDate之差,作为本次失败的时长,并将其累加到该实体的总失败时长中。
      • 计算完成后,将lastFailDate重置为null,表示该失败周期已经结束。
    • 如果lastFailDate为null,则忽略当前成功记录,因为它没有前置的失败周期。

使用Java Stream API实现

Java 8引入的Stream API为集合处理提供了强大而灵活的工具。我们可以利用它来实现上述逻辑。

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

public class FailureDurationCalculator {

    public static Map<String, Integer> calculateFailureDurationWithStream(List<Record> records) {
        return records.stream()
            // 1. 按名称分组:将所有记录根据其name字段分组,得到Map<String, List<Record>>
            .collect(Collectors.groupingBy(r -> r.name))
            .entrySet().stream() // 将Map的entrySet转换为Stream,以便进一步处理每个分组
            // 2. 对每个分组计算失败时长,并收集到最终的Map<String, Integer>中
            .collect(Collectors.toMap(Map.Entry::getKey, entry -> {
                // 使用数组作为可变变量,以在Lambda表达式中存储上一次失败的日期。
                // Stream操作通常是无状态的,但这里需要维护一个跨记录的状态。
                Integer[] lastFailDate = new Integer[]{null};

                return entry.getValue().stream()
                    // 3. 对每个分组内的记录按日期升序排序
                    .sorted(Comparator.comparing(r -> r.date))
                    .mapToInt(record -> {
                        if ("fail".equals(record.status) && lastFailDate[0] == null) {
                            // 遇到失败,且当前没有正在进行的失败期,记录失败开始日期
                            lastFailDate[0] = record.date;
                        } else if ("success".equals(record.status) && lastFailDate[0] != null) {
                            // 遇到成功,且有正在进行的失败期,计算时长
                            int duration = record.date - lastFailDate[0];
                            lastFailDate[0] = null; // 重置失败开始日期,表示该失败周期已结束
                            return duration;
                        }
                        return 0; // 其他情况(如连续失败、成功后无失败等)不产生时长
                    })
                    .sum(); // 累加所有计算出的失败时长,得到该实体的总失败时长
            }));
    }

    public static void main(String[] args) {
        List<Record> records = List.of(
            new Record("john", 2015, "success"),
            new Record("john", 2013, "fail"),
            new Record("chris", 2013, "success"),
            new Record("john", 2012, "fail"),
            new Record("john", 2009, "success"),
            new Record("chris", 2007, "fail"),
            new Record("john", 2005, "fail")
        );

        Map<String, Integer> failureDurations = calculateFailureDurationWithStream(records);
        System.out.println("使用Stream API计算的失败时长: " + failureDurations); // 预期输出: {chris=6, john=7}
    }
}
登录后复制

以上就是Java中计算列表数据中按条件分组的连续失败时长的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号