0

0

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

霞舞

霞舞

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

|

190人浏览过

|

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

Misum AI
Misum AI

一站式聚合多模型AI问答工具

下载
  • 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工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

252

2023.09.22

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

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

1049

2024.03.01

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

26

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

68

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

164

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

84

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

113

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

29

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

79

2026.02.28

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.2万人学习

C# 教程
C# 教程

共94课时 | 10.9万人学习

Java 教程
Java 教程

共578课时 | 78.6万人学习

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

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