用Java统计头球争顶成功率案例
下面给你一个完整的Java实现方案,包含数据模型、统计算法、示例代码,可以直接运行。

需求分析
头球争顶成功率公式:
成功率 = 成功争顶次数 / 总争顶次数 × 100%
通常需要按球员、球队、比赛等维度统计。
数据模型
import java.util.*;
/**
* 争顶事件(一次头球争顶)
*/
class AerialDuel {
private String playerName; // 球员姓名
private String team; // 所属球队
private boolean won; // 是否争顶成功
public AerialDuel(String playerName, String team, boolean won) {
this.playerName = playerName;
this.team = team;
this.won = won;
}
public String getPlayerName() { return playerName; }
public String getTeam() { return team; }
public boolean isWon() { return won; }
}
统计结果封装
class AerialStat {
private String name;
private int total; // 总争顶次数
private int won; // 成功次数
public AerialStat(String name) {
this.name = name;
}
public void add(boolean isWon) {
total++;
if (isWon) won++;
}
/** 成功率(百分比,保留一位小数) */
public double getSuccessRate() {
if (total == 0) return 0.0;
return Math.round(won * 1000.0 / total) / 10.0;
}
public String getName() { return name; }
public int getTotal() { return total; }
public int getWon() { return won; }
@Override
public String toString() {
return String.format("%-10s 争顶:%2d 成功:%2d 成功率:%.1f%%",
name, total, won, getSuccessRate());
}
}
统计核心类
import java.util.*;
import java.util.stream.Collectors;
public class AerialDuelAnalyzer {
private final List<AerialDuel> duels = new ArrayList<>();
public void addDuel(AerialDuel duel) {
duels.add(duel);
}
/** 按球员统计 */
public Map<String, AerialStat> statByPlayer() {
Map<String, AerialStat> map = new LinkedHashMap<>();
for (AerialDuel d : duels) {
map.computeIfAbsent(d.getPlayerName(), AerialStat::new)
.add(d.isWon());
}
return map;
}
/** 按球队统计 */
public Map<String, AerialStat> statByTeam() {
Map<String, AerialStat> map = new LinkedHashMap<>();
for (AerialDuel d : duels) {
map.computeIfAbsent(d.getTeam(), AerialStat::new)
.add(d.isWon());
}
return map;
}
/** 按球员统计并排序(成功率降序) */
public List<AerialStat> rankByPlayer() {
return statByPlayer().values().stream()
.sorted(Comparator.comparingDouble(AerialStat::getSuccessRate).reversed())
.collect(Collectors.toList());
}
}
测试运行
public class Main {
public static void main(String[] args) {
AerialDuelAnalyzer analyzer = new AerialDuelAnalyzer();
// 模拟一场比赛的头球争顶数据
// 球员A:5次争顶,3次成功
analyzer.addDuel(new AerialDuel("张三", "红队", true));
analyzer.addDuel(new AerialDuel("张三", "红队", true));
analyzer.addDuel(new AerialDuel("张三", "红队", false));
analyzer.addDuel(new AerialDuel("张三", "红队", true));
analyzer.addDuel(new AerialDuel("张三", "红队", false));
// 球员B:4次争顶,4次成功
analyzer.addDuel(new AerialDuel("李四", "红队", true));
analyzer.addDuel(new AerialDuel("李四", "红队", true));
analyzer.addDuel(new AerialDuel("李四", "红队", true));
analyzer.addDuel(new AerialDuel("李四", "红队", true));
// 球员C(蓝队):3次争顶,1次成功
analyzer.addDuel(new AerialDuel("王五", "蓝队", false));
analyzer.addDuel(new AerialDuel("王五", "蓝队", true));
analyzer.addDuel(new AerialDuel("王五", "蓝队", false));
// 球员D(蓝队):2次争顶,1次成功
analyzer.addDuel(new AerialDuel("赵六", "蓝队", false));
analyzer.addDuel(new AerialDuel("赵六", "蓝队", true));
// ===== 输出按球员排名 =====
System.out.println("===== 球员头球争顶成功率排名 =====");
analyzer.rankByPlayer().forEach(System.out::println);
// ===== 输出按球队统计 =====
System.out.println("\n===== 球队头球争顶成功率 =====");
analyzer.statByTeam().forEach((team, stat) -> System.out.println(stat));
}
}
运行结果
===== 球员头球争顶成功率排名 =====
李四 争顶: 4 成功: 4 成功率:100.0%
张三 争顶: 5 成功: 3 成功率:60.0%
赵六 争顶: 2 成功: 1 成功率:50.0%
王五 争顶: 3 成功: 1 成功率:33.3%
===== 球队头球争顶成功率 =====
红队 争顶: 9 成功: 7 成功率:77.8%
蓝队 争顶: 5 成功: 2 成功率:40.0%
进阶扩展建议
| 扩展点 | 说明 |
|---|---|
| 数据来源 | 从 CSV/数据库/JSON 读取比赛事件 |
| 分区域统计 | 增加 zone 字段(前场/中场/后场)分析争顶位置 |
| 时间维度 | 增加 minute 字段,统计各时段成功率 |
| SQL 实现 | SELECT player, COUNT(*), SUM(won)/COUNT(*)*100 FROM duels GROUP BY player |
| 可视化 | 导出结果到 Excel(Apache POI)或用 JFreeChart 画柱状图 |
| 多场比赛 | 传入选定的 matchId 过滤后再统计 |
关键设计要点
- 用
computeIfAbsent+ 累加器 避免了多次遍历的复杂度,O(n) 一次扫描完成统计。 - *成功率计算用 `Math.round(x1000)/10.0`** 保证保留一位小数且不产生浮点误差显示问题。
- 返回
LinkedHashMap保留球员首次出现的顺序,便于阅读;需要排序时用Stream.sorted。 - 模型与统计分离,方便后面接真实数据源(爬虫、API、DB)。
如果你有真实的数据格式(Opta、SofaScore 导出 CSV),我可以帮你写针对该格式的解析 + 统计完整代码。