讲师中心 微信公众号
AI工具推荐 视频效率加速

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

秋萱小哥_6637

秋萱小哥_6637

发布时间:2025-12-12 21:18:01

|

210人浏览过

|

来源于php中文网

原创

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免费学习笔记(深入)”;

Livestream Script Generator
Livestream Script Generator

直播带货脚本:话术、节奏、互动、促单。适用于主播、电商运营、MCN机构。

下载
  • 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}
    }
}

热门AI工具

更多
讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

40

2026.10.28

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

120

2026.10.28

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

140

2026.10.22

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

120

2026.10.22

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

80

2026.10.22

Gradle怎么构建Java项目
Gradle怎么构建Java项目

本专题介绍Gradle Java项目构建方法,涵盖Java插件配置、依赖声明、源码编译、测试执行和打包检查,帮助你理解各阶段的任务关系,逐步完成项目构建。

40

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn