
本教程详细阐述了如何在java中计算一组时间序列记录中每个实体的累计失败持续时间。通过将数据按实体分组并按日期排序,我们利用java stream api或seq库来识别失败周期,并计算从失败开始到下一个成功状态的持续时间,同时考虑了截止到特定年份的未结束失败周期。
在实际业务场景中,我们经常需要从一系列事件或状态记录中提取有意义的统计数据。本教程旨在解决一个具体的问题:给定一组包含名称、日期和状态(“success”或“fail”)的记录,如何计算每个名称的总失败持续时间。
问题定义: 失败持续时间被定义为从一个“fail”状态的日期开始,直到下一个“success”状态的日期结束的时间跨度。如果一个实体在经历“fail”状态后,在下一个“success”状态出现之前,又连续出现多个“fail”状态,则这些连续的“fail”状态被视为同一个失败周期的一部分。此外,如果一个失败周期在数据集的末尾仍然没有遇到“success”状态,那么它的持续时间应计算到指定的结束年份(例如,本例中为2022年)。
示例说明: 考虑以下数据:
[
{"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:
对于chris:
本教程将详细介绍如何使用Java高效地实现这一计算逻辑,并特别关注如何处理那些在数据末尾仍未结束的失败周期。
立即学习“Java免费学习笔记(深入)”;
为了更好地组织和处理数据,我们建议使用一个自定义的Java类来表示每一条记录,而不是直接使用HashMap。这样做可以提高代码的类型安全性、可读性和可维护性。
public class Record {
public String name;
public Integer date;
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 + '\'' +
'}';
}
}核心的计算逻辑包括以下几个步骤:
下面是使用Java Stream API实现这一逻辑的示例代码:
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class FailureDurationCalculator {
// 定义数据模型(同上)
public static class Record {
public String name;
public Integer date;
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 + '\'' +
'}';
}
}
/**
* 使用Java Stream API计算每个实体的失败持续时间。
*
* @param records 原始记录列表。
* @param targetYear 用于计算未结束失败周期的截止年份。
* @return 包含每个名称及其总失败持续时间的Map。
*/
public static Map<String, Integer> calculateFailureDuration(List<Record> records, int targetYear) {
return records.stream()
// 1. 按名称分组
.collect(Collectors.groupingBy(r -> r.name))
.entrySet().stream()
// 2. 将分组结果转换为Map<String, Integer>
.collect(Collectors.toMap(Map.Entry::getKey, entry -> {
// 使用数组作为可变引用,以便在lambda表达式中修改
Integer[] lastFailDate = new Integer[]{null};
// 3. 对每个分组内的记录按日期排序,并计算失败持续时间
int totalDuration = entry.getValue().stream()
.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(); // 累加所有已结束失败周期的持续时间
// 4. 处理未结束的失败周期
if (lastFailDate[0] != null) {
// 如果在所有记录处理完毕后,lastFailDate仍不为null,
// 说明存在一个从lastFailDate开始,持续到targetYear的失败周期
totalDuration += (targetYear - lastFailDate[0]);
}
return totalDuration;
}));
}
public static void main(String[] args) {
List<Record> records = Arrays.asList(
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"),
new Record("alice", 2010, "fail"), // 新增Alice,演示未结束失败
new Record("bob", 2000, "fail"),
new Record("bob", 2002, "fail") // 新增Bob,演示连续失败且未结束
);
int targetYear = 2022; // 截止年份
Map<String, Integer> failureDurations = calculateFailureDuration(records, targetYear);
System.out.println("计算结果: " + failureDurations);
// 预期输出 (基于上述数据和targetYear=2022):
// john: 7 (2009-2005=4, 2015-2012=3)
// chris: 6 (2013-2007=6)
// alice: 12 (2022-2010=12)
// bob: 22 (2022-2000=22, 连续失败只算第一次开始)
}
}除了标准的Java Stream API,一些第三方库如Seq提供了更流畅、更函数式的集合处理方式。如果您倾向于使用此类库,以下是使用Seq实现相同逻辑的示例。请注意,使用Seq需要先将其添加到您的项目依赖中。
import java.util.Arrays;
import java.util.List;
import java.util.Map;
// 假设已引入 Seq 库
// import com.github.wolray.seq.Seq;
public class FailureDurationCalculatorSeq {
// 定义数据模型(同上)
public static class Record {
public String name;
public Integer date;
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 + '\'' +
'}';
}
}
/**
* 使用Seq库计算每个实体的失败持续时间。
*
* @param records 原始记录列表。
* @param targetYear 用于计算未结束失败周期的截止年份。
* @return 包含每个名称及其总失败持续时间的Map。
*/
public static Map<String, Integer> calculateFailureDurationWithSeq(List<Record> records, int targetYear) {
// 假设Seq.of(records) 可用
return Seq.of(records)
.groupBy(r -> r.name) // 按名称分组
.toList() // 将分组结果转换为List<Map.Entry<String, List<Record>>>
.toMap(Map.Entry::getKey, entry -> { // 转换为最终结果Map
Integer[] lastFailDate = new Integer[]{null};
int totalDuration = Seq.of(entry.getValue())
.sortBy(r -> r.date) // 排序
.sumInt(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;
});
if (lastFailDate[0] != null) {
totalDuration += (targetYear - lastFailDate[0]);
}
return totalDuration;
});
}
// main方法同上,只需调用 calculateFailureDurationWithSeq
// 为了运行此代码,您需要将Seq库添加到您的项目中,并取消注释相关的import语句。
// 这里不再重复main方法,其调用方式与Stream API版本类似。
}Seq库提供了与Stream API类似的功能,但在某些情况下可能提供更简洁的语法。核心逻辑保持不变,即分组、排序、状态跟踪和累加。
在实现和应用上述解决方案时,需要考虑以下几点:
以上就是Java中计算列表式数据中实体失败持续时间的教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号