Java 案例:统计慢跑恢复时间数据
下面给你一个完整可运行的 Java 案例,用于统计慢跑后的恢复时间数据(例如跑完 5 公里后心率恢复到静息心率所需时间),包含数据建模、统计计算、结果输出。

需求说明
慢跑恢复时间(Recovery Time)通常指:运动结束后,心率恢复到某一阈值(如静息心率 +10 bpm)所需的时间(秒或分钟)。
我们要统计:
- 每次跑步的恢复时间
- 平均值、最大值、最小值
- 中位数
- 标准差
- 达标率(如恢复时间 < 5 分钟的比例)
代码实现
import java.util.*;
import java.util.stream.Collectors;
/**
* 慢跑恢复时间统计案例
*/
public class JoggingRecoveryStats {
/** 单次跑步记录 */
static class RunRecord {
String date; // 日期
double distanceKm; // 距离(km)
int recoverySeconds; // 恢复时间(秒)
int restHeartRate; // 静息心率
int peakHeartRate; // 峰值心率
public RunRecord(String date, double distanceKm, int recoverySeconds,
int restHeartRate, int peakHeartRate) {
this.date = date;
this.distanceKm = distanceKm;
this.recoverySeconds = recoverySeconds;
this.restHeartRate = restHeartRate;
this.peakHeartRate = peakHeartRate;
}
}
/** 统计结果封装 */
static class StatsResult {
int count;
double avg;
int min;
int max;
double median;
double stdDev;
double passRate; // 达标率(%)
int passCount;
}
public static void main(String[] args) {
// 1. 模拟数据(真实项目可从数据库/CSV/文件读取)
List<RunRecord> records = Arrays.asList(
new RunRecord("2024-05-01", 5.0, 180, 62, 158),
new RunRecord("2024-05-03", 5.2, 210, 60, 162),
new RunRecord("2024-05-05", 4.8, 150, 63, 155),
new RunRecord("2024-05-08", 5.0, 240, 61, 165),
new RunRecord("2024-05-10", 5.5, 300, 59, 170),
new RunRecord("2024-05-12", 5.0, 195, 62, 160),
new RunRecord("2024-05-15", 6.0, 330, 60, 172),
new RunRecord("2024-05-18", 5.0, 165, 64, 156),
new RunRecord("2024-05-20", 5.3, 225, 61, 163),
new RunRecord("2024-05-22", 5.0, 270, 63, 168)
);
// 2. 计算统计指标(达标:恢复时间 <= 240 秒,即 4 分钟)
StatsResult result = analyze(records, 240);
// 3. 打印详细记录
printRecords(records);
// 4. 打印统计结果
printStats(result);
// 5. 按距离分组统计
System.out.println("\n=== 按距离分组平均恢复时间 ===");
Map<Double, Double> byDistance = records.stream()
.collect(Collectors.groupingBy(
r -> r.distanceKm,
TreeMap::new,
Collectors.averagingInt(r -> r.recoverySeconds)));
byDistance.forEach((dist, avg) ->
System.out.printf("距离 %.1f km → 平均恢复 %.0f 秒%n", dist, avg));
}
/** 核心统计逻辑 */
static StatsResult analyze(List<RunRecord> records, int passThreshold) {
StatsResult r = new StatsResult();
r.count = records.size();
IntSummaryStatistics summary = records.stream()
.mapToInt(x -> x.recoverySeconds)
.summaryStatistics();
r.avg = summary.getAverage();
r.min = summary.getMin();
r.max = summary.getMax();
// 中位数
int[] sorted = records.stream()
.mapToInt(x -> x.recoverySeconds)
.sorted()
.toArray();
int n = sorted.length;
r.median = (n % 2 == 0)
? (sorted[n / 2 - 1] + sorted[n / 2]) / 2.0
: sorted[n / 2];
// 标准差
double variance = records.stream()
.mapToDouble(x -> Math.pow(x.recoverySeconds - r.avg, 2))
.sum() / n;
r.stdDev = Math.sqrt(variance);
// 达标率
r.passCount = (int) records.stream()
.filter(x -> x.recoverySeconds <= passThreshold)
.count();
r.passRate = r.passCount * 100.0 / n;
return r;
}
static void printRecords(List<RunRecord> records) {
System.out.println("=== 慢跑恢复时间明细 ===");
System.out.printf("%-12s %-8s %-12s %-10s %-10s%n",
"日期", "距离(km)", "恢复(秒)", "静息心率", "峰值心率");
for (RunRecord r : records) {
System.out.printf("%-12s %-8.1f %-12d %-10d %-10d%n",
r.date, r.distanceKm, r.recoverySeconds,
r.restHeartRate, r.peakHeartRate);
}
}
static void printStats(StatsResult r) {
System.out.println("\n=== 恢复时间统计结果 ===");
System.out.println("样本数量 : " + r.count);
System.out.printf("平均值 : %.2f 秒 (%.2f 分钟)%n", r.avg, r.avg / 60);
System.out.println("最小值 : " + r.min + " 秒");
System.out.println("最大值 : " + r.max + " 秒");
System.out.printf("中位数 : %.2f 秒%n", r.median);
System.out.printf("标准差 : %.2f 秒%n", r.stdDev);
System.out.printf("达标次数 : %d / %d (%.1f%%)%n",
r.passCount, r.count, r.passRate);
}
}
运行输出示例
=== 慢跑恢复时间明细 ===
日期 距离(km) 恢复(秒) 静息心率 峰值心率
2024-05-01 5.0 180 62 158
2024-05-03 5.2 210 60 162
...
=== 恢复时间统计结果 ===
样本数量 : 10
平均值 : 226.50 秒 (3.78 分钟)
最小值 : 150 秒
最大值 : 330 秒
中位数 : 217.50 秒
标准差 : 55.34 秒
达标次数 : 7 / 10 (70.0%)
=== 按距离分组平均恢复时间 ===
距离 4.8 km → 平均恢复 150 秒
距离 5.0 km → 平均恢复 219 秒
距离 5.2 km → 平均恢复 210 秒
...
核心知识点
| 技术点 | 说明 |
|---|---|
IntSummaryStatistics |
一次性拿到 count / sum / avg / min / max |
stream().mapToInt() |
提取恢复时间字段 |
sorted() + 数组下标 |
求中位数 |
| 数学公式 | 标准差 = √(Σ(x-μ)²/n) |
groupingBy + TreeMap |
按距离分组且有序 |
| 达标率 | filter().count() / 总数 |
扩展建议(生产级)
- 数据来源:改为从 CSV / MySQL / Excel 读取(用 OpenCSV、JDBC、EasyExcel)。
- 时间序列分析:加入
LocalDate按周/月聚合,观察恢复能力趋势。 - 异常检测:恢复时间 > 均值+2σ 视为异常,提示过度训练。
- 可视化:输出 JSON 交给前端图表(ECharts / Chart.js)。
- 多用户支持:给
RunRecord加userId,用groupingBy(RunRecord::getUserId)。 - 单位转换:同时输出秒、分钟、
mm:ss格式。
需要我把这个案例改成从 CSV 文件读取、接入 Spring Boot REST 接口,还是加上图表数据导出 JSON 的版本?告诉我方向即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播