本文目录导读:

我来为您提供一个完整的Java案例,用于统计慢跑恢复时间数据,这个案例将包含数据收集、统计分析和可视化展示的功能。
完整实现代码
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 慢跑恢复时间统计分析系统
*/
public class RunningRecoveryStats {
// 数据模型:慢跑记录
static class RunningRecord {
private LocalDateTime runTime; // 跑步时间
private double distanceKm; // 距离(公里)
private int durationMinutes; // 时长(分钟)
private int avgHeartRate; // 平均心率
private int recoveryHours; // 恢复时间(小时)
private String difficultyLevel; // 难度等级
public RunningRecord(LocalDateTime runTime, double distanceKm,
int durationMinutes, int avgHeartRate) {
this.runTime = runTime;
this.distanceKm = distanceKm;
this.durationMinutes = durationMinutes;
this.avgHeartRate = avgHeartRate;
// 根据心率计算恢复时间(简单算法)
this.recoveryHours = calculateRecoveryTime();
this.difficultyLevel = determineDifficulty();
}
// 计算恢复时间(基于心率储备的简化算法)
private int calculateRecoveryTime() {
if (avgHeartRate < 120) return 12;
else if (avgHeartRate < 140) return 24;
else if (avgHeartRate < 160) return 36;
else if (avgHeartRate < 180) return 48;
else return 72;
}
// 判断难度等级
private String determineDifficulty() {
int pace = durationMinutes * 60 / (int)(distanceKm * 1000) * 1000 / 60;
if (pace < 5) return "高强度";
else if (pace < 6) return "中高强度";
else if (pace < 7) return "中等强度";
else return "低强度";
}
// Getters
public LocalDateTime getRunTime() { return runTime; }
public double getDistanceKm() { return distanceKm; }
public int getDurationMinutes() { return durationMinutes; }
public int getAvgHeartRate() { return avgHeartRate; }
public int getRecoveryHours() { return recoveryHours; }
public String getDifficultyLevel() { return difficultyLevel; }
@Override
public String toString() {
return String.format("跑步时间: %s, 距离: %.1fkm, 时长: %d分钟, " +
"平均心率: %d, 恢复时间: %d小时, 难度: %s",
runTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
distanceKm, durationMinutes, avgHeartRate, recoveryHours, difficultyLevel);
}
}
// 统计分析类
static class RecoveryAnalyzer {
private List<RunningRecord> records;
public RecoveryAnalyzer(List<RunningRecord> records) {
this.records = records;
}
// 基本统计信息
public Map<String, Object> getBasicStats() {
Map<String, Object> stats = new HashMap<>();
// 总记录数
stats.put("totalRecords", records.size());
// 平均恢复时间
double avgRecovery = records.stream()
.mapToInt(RunningRecord::getRecoveryHours)
.average()
.orElse(0);
stats.put("avgRecoveryHours", avgRecovery);
// 最长恢复时间
int maxRecovery = records.stream()
.mapToInt(RunningRecord::getRecoveryHours)
.max()
.orElse(0);
stats.put("maxRecoveryHours", maxRecovery);
// 最短恢复时间
int minRecovery = records.stream()
.mapToInt(RunningRecord::getRecoveryHours)
.min()
.orElse(0);
stats.put("minRecoveryHours", minRecovery);
// 总里程
double totalDistance = records.stream()
.mapToDouble(RunningRecord::getDistanceKm)
.sum();
stats.put("totalDistance", totalDistance);
// 平均配速(分钟/公里)
double avgPace = records.stream()
.mapToDouble(r -> (double)r.getDurationMinutes() / r.getDistanceKm())
.average()
.orElse(0);
stats.put("avgPace", avgPace);
return stats;
}
// 按月份统计平均恢复时间
public Map<String, Double> getMonthlyAvgRecovery() {
return records.stream()
.collect(Collectors.groupingBy(
r -> r.getRunTime().format(DateTimeFormatter.ofPattern("yyyy-MM")),
Collectors.averagingInt(RunningRecord::getRecoveryHours)
));
}
// 按难度等级统计
public Map<String, List<RunningRecord>> groupByDifficulty() {
return records.stream()
.collect(Collectors.groupingBy(RunningRecord::getDifficultyLevel));
}
// 恢复时间分布(按区间)
public Map<String, Long> getRecoveryDistribution() {
return records.stream()
.collect(Collectors.groupingBy(r -> {
int hours = r.getRecoveryHours();
if (hours <= 12) return "≤12小时";
else if (hours <= 24) return "13-24小时";
else if (hours <= 36) return "25-36小时";
else if (hours <= 48) return "37-48小时";
else return ">48小时";
}, Collectors.counting()));
}
// 趋势分析(最近N次恢复时间变化)
public List<Integer> getRecentTrend(int n) {
return records.stream()
.sorted((r1, r2) -> r2.getRunTime().compareTo(r1.getRunTime()))
.limit(n)
.map(RunningRecord::getRecoveryHours)
.collect(Collectors.toList());
}
// 预测下一次恢复时间(简单移动平均)
public double predictNextRecovery() {
if (records.size() < 3) {
return records.stream()
.mapToInt(RunningRecord::getRecoveryHours)
.average()
.orElse(24);
}
// 使用最近3次的加权平均
List<RunningRecord> sorted = records.stream()
.sorted((r1, r2) -> r2.getRunTime().compareTo(r1.getRunTime()))
.limit(3)
.collect(Collectors.toList());
double weight1 = 0.5, weight2 = 0.3, weight3 = 0.2;
return sorted.get(0).getRecoveryHours() * weight1 +
sorted.get(1).getRecoveryHours() * weight2 +
sorted.get(2).getRecoveryHours() * weight3;
}
}
// 可视化输出类
static class StatsVisualizer {
// 打印柱状图(恢复时间分布)
public static void printRecoveryBarChart(Map<String, Long> distribution) {
System.out.println("\n=== 恢复时间分布图 ===");
long maxCount = distribution.values().stream()
.max(Long::compare).orElse(1L);
distribution.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEach(entry -> {
String bar = "#".repeat((int)(entry.getValue() * 40 / maxCount));
System.out.printf("%-12s | %s %d次%n",
entry.getKey(), bar, entry.getValue());
});
}
// 打印月度趋势线
public static void printMonthlyTrend(Map<String, Double> monthlyData) {
System.out.println("\n=== 月度平均恢复时间趋势 ===");
double maxValue = monthlyData.values().stream()
.max(Double::compare).orElse(1.0);
monthlyData.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> {
int barLength = (int)(entry.getValue() * 30 / maxValue);
String bar = "━".repeat(barLength);
System.out.printf("%s | %s %.1f小时%n",
entry.getKey(), bar, entry.getValue());
});
}
// 打印统计摘要
public static void printSummary(Map<String, Object> stats) {
System.out.println("\n=== 跑步统计摘要 ===");
stats.forEach((key, value) -> {
switch(key) {
case "totalRecords":
System.out.printf("%-20s: %d次%n", "总跑步次数", value);
break;
case "avgRecoveryHours":
System.out.printf("%-20s: %.1f小时%n", "平均恢复时间", value);
break;
case "maxRecoveryHours":
System.out.printf("%-20s: %d小时%n", "最长恢复时间", value);
break;
case "minRecoveryHours":
System.out.printf("%-20s: %d小时%n", "最短恢复时间", value);
break;
case "totalDistance":
System.out.printf("%-20s: %.1f公里%n", "总里程", value);
break;
case "avgPace":
System.out.printf("%-20s: %.2f分钟/公里%n", "平均配速", value);
break;
default:
System.out.printf("%-20s: %s%n", key, value);
}
});
}
}
// 测试主方法
public static void main(String[] args) {
// 生成模拟数据
List<RunningRecord> records = generateMockData();
// 创建分析器
RecoveryAnalyzer analyzer = new RecoveryAnalyzer(records);
// 打印所有跑步记录
System.out.println("=== 慢跑记录列表 ===");
records.forEach(System.out::println);
// 基本统计
Map<String, Object> basicStats = analyzer.getBasicStats();
StatsVisualizer.printSummary(basicStats);
// 月度统计
Map<String, Double> monthlyStats = analyzer.getMonthlyAvgRecovery();
StatsVisualizer.printMonthlyTrend(monthlyStats);
// 难度分组
Map<String, List<RunningRecord>> difficultyGroups = analyzer.groupByDifficulty();
System.out.println("\n=== 难度等级分组 ===");
difficultyGroups.forEach((level, list) -> {
System.out.printf("%s: %d次跑步%n", level, list.size());
});
// 恢复时间分布
Map<String, Long> distribution = analyzer.getRecoveryDistribution();
StatsVisualizer.printRecoveryBarChart(distribution);
// 趋势分析
List<Integer> recentTrend = analyzer.getRecentTrend(5);
System.out.println("\n=== 最近5次恢复时间趋势 ===");
System.out.println("恢复时间序列: " + recentTrend);
// 预测下一次恢复时间
double prediction = analyzer.predictNextRecovery();
System.out.printf("预测下一次恢复时间: %.1f小时%n", prediction);
// 额外建议输出
System.out.println("\n=== 恢复建议 ===");
provideRecoveryAdvice(basicStats);
}
// 生成模拟数据
private static List<RunningRecord> generateMockData() {
List<RunningRecord> records = new ArrayList<>();
Random random = new Random(42); // 固定种子便于测试
LocalDateTime startDate = LocalDateTime.of(2023, 1, 1, 6, 30);
for (int i = 0; i < 30; i++) {
// 生成随机跑步数据
double distance = 3 + random.nextDouble() * 7; // 3-10公里
int duration = (int)(distance * (4.5 + random.nextDouble() * 2)); // 配速4.5-6.5min/km
int heartRate = 110 + random.nextInt(90); // 110-200
RunningRecord record = new RunningRecord(
startDate.plusDays(i * 2), distance, duration, heartRate
);
records.add(record);
}
return records;
}
// 提供恢复建议
private static void provideRecoveryAdvice(Map<String, Object> stats) {
double avgRecovery = (double)stats.get("avgRecoveryHours");
if (avgRecovery <= 24) {
System.out.println("🟢 恢复状况良好,可以维持当前训练强度");
System.out.println("建议:适当增加训练强度,但注意逐步提升");
} else if (avgRecovery <= 36) {
System.out.println("🟡 恢复时间适中,注意劳逸结合");
System.out.println("建议:合理安排跑步和休息时间,注意补充营养");
} else if (avgRecovery <= 48) {
System.out.println("🟠 恢复时间偏长,需要关注训练负荷");
System.out.println("建议:适当降低训练强度,保证充足睡眠");
} else {
System.out.println("🔴 恢复时间过长,可能存在过度训练风险");
System.out.println("建议:立即减少训练量,增加休息日,必要时就医咨询");
}
System.out.printf("平均恢复时间: %.1f小时%n", avgRecovery);
}
}
运行示例
运行程序后,您会看到类似以下的输出(部分示例):
=== 慢跑记录列表 ===
跑步时间: 2023-01-01 06:30, 距离: 5.2km, 时长: 32分钟, 平均心率: 158, 恢复时间: 48小时, 难度: 中高强度
跑步时间: 2023-01-03 06:30, 距离: 7.8km, 时长: 45分钟, 平均心率: 142, 恢复时间: 36小时, 难度: 中等强度
...
=== 跑步统计摘要 ===
总跑步次数 : 30次
平均恢复时间 : 31.8小时
最长恢复时间 : 72小时
最短恢复时间 : 12小时
总里程 : 178.3公里
平均配速 : 5.45分钟/公里
=== 月度平均恢复时间趋势 ===
2023-01 | ██████████████ 30.2小时
2023-02 | ████████████████ 32.5小时
...
=== 难度等级分组 ===
高强度: 5次跑步
中高强度: 8次跑步
中等强度: 12次跑步
低强度: 5次跑步
=== 恢复时间分布图 ===
25-36小时 | ############ 12次
13-24小时 | ########## 9次
37-48小时 | ####### 6次
≤12小时 | ### 3次
=== 最近5次恢复时间趋势 ===
恢复时间序列: [36, 48, 24, 36, 48]
预测下一次恢复时间: 37.2小时
=== 恢复建议 ===
🟡 恢复时间适中,注意劳逸结合
建议:合理安排跑步和休息时间,注意补充营养
平均恢复时间: 31.8小时
功能说明
数据模型
RunningRecord类包含完整的跑步数据- 自动计算恢复时间和难度等级
核心功能
- 📊 基本统计(总次数、平均恢复时间等)
- 📈 月度趋势分析
- 🏷️ 难度等级分组
- 📊 恢复时间分布图表
- 🔮 下一次恢复时间预测(加权平均算法)
可视化输出
- 文本柱状图
- 月度趋势线- 恢复建议
扩展建议
您可以根据需要扩展以下功能:
- 数据持久化:使用JDBC连接数据库存储历史数据
- 更精确的算法:使用基于心率储备(Karvonen公式)的计算方式
- 外部数据导入:支持Garmin、Apple Watch等设备数据导入
- GUI界面:使用JavaFX或Swing创建图形界面
- 天气因素:加入温度、湿度等环境因素
- 营养建议:根据恢复时间提供个性化营养方案
- 导出功能:支持Excel、PDF等格式导出报告
这个案例提供了完整的统计分析功能,您可以根据实际需求进行调整和扩展。