足球直塞球成功率统计案例
下面用 Java 写一个统计直塞球(Through Ball / 直塞)成功率的完整案例,包含数据模型、统计逻辑和多种统计维度。

需求说明
直塞球成功率 = 成功直塞次数 / 直塞总次数 × 100%
- 一次直塞尝试:球员向前传出一脚穿透防线的球
- 成功:接球队员成功拿到球(未被拦截、未出界)
- 失败:被防守队员拦截、传球失误、出界等
完整代码
import java.util.*;
import java.util.stream.Collectors;
// 传球结果枚举
enum PassResult {
SUCCESS, // 成功
INTERCEPTED, // 被拦截
OUT, // 出界
FAILED // 其他失误
}
// 传球记录
class PassRecord {
private String playerName; // 传球球员
private String team; // 球队
private String passType; // 传球类型:THROUGH_BALL(直塞) / NORMAL(普通)
private PassResult result; // 结果
public PassRecord(String playerName, String team, String passType, PassResult result) {
this.playerName = playerName;
this.team = team;
this.passType = passType;
this.result = result;
}
public String getPlayerName() { return playerName; }
public String getTeam() { return team; }
public String getPassType() { return passType; }
public PassResult getResult() { return result; }
}
public class ThroughBallStats {
public static void main(String[] args) {
// 1. 模拟数据
List<PassRecord> records = Arrays.asList(
new PassRecord("德布劳内", "曼城", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("德布劳内", "曼城", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("德布劳内", "曼城", "THROUGH_BALL", PassResult.INTERCEPTED),
new PassRecord("德布劳内", "曼城", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("德布劳内", "曼城", "THROUGH_BALL", PassResult.OUT),
new PassRecord("B席", "曼城", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("B席", "曼城", "THROUGH_BALL", PassResult.FAILED),
new PassRecord("B席", "曼城", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("厄德高", "阿森纳", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("厄德高", "阿森纳", "THROUGH_BALL", PassResult.INTERCEPTED),
new PassRecord("厄德高", "阿森纳", "THROUGH_BALL", PassResult.SUCCESS),
new PassRecord("厄德高", "阿森纳", "THROUGH_BALL", PassResult.SUCCESS),
// 普通传球不参与直塞统计
new PassRecord("德布劳内", "曼城", "NORMAL", PassResult.SUCCESS)
);
// 2. 总体直塞成功率
System.out.println("========== 总体直塞成功率 ==========");
printStats(records);
// 3. 按球员统计
System.out.println("\n========== 各球员直塞成功率 ==========");
Map<String, List<PassRecord>> byPlayer = records.stream()
.filter(r -> "THROUGH_BALL".equals(r.getPassType()))
.collect(Collectors.groupingBy(PassRecord::getPlayerName));
byPlayer.forEach((player, list) -> {
System.out.println("球员:" + player);
printStats(list);
});
// 4. 按球队统计
System.out.println("\n========== 各球队直塞成功率 ==========");
Map<String, List<PassRecord>> byTeam = records.stream()
.filter(r -> "THROUGH_BALL".equals(r.getPassType()))
.collect(Collectors.groupingBy(PassRecord::getTeam));
byTeam.forEach((team, list) -> {
System.out.println("球队:" + team);
printStats(list);
});
// 5. 排名(按成功率从高到低)
System.out.println("\n========== 球员直塞成功率排名 ==========");
byPlayer.entrySet().stream()
.map(e -> {
long total = e.getValue().size();
long success = e.getValue().stream()
.filter(r -> r.getResult() == PassResult.SUCCESS).count();
double rate = total == 0 ? 0 : success * 100.0 / total;
return new AbstractMap.SimpleEntry<>(e.getKey(), rate);
})
.sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))
.forEach(e -> System.out.printf("%s:%.1f%%%n", e.getKey(), e.getValue()));
}
/** 打印某组直塞传球的统计信息 */
private static void printStats(List<PassRecord> records) {
long total = records.stream()
.filter(r -> "THROUGH_BALL".equals(r.getPassType()))
.count();
if (total == 0) {
System.out.println(" 无直塞数据");
return;
}
long success = records.stream()
.filter(r -> "THROUGH_BALL".equals(r.getPassType()))
.filter(r -> r.getResult() == PassResult.SUCCESS)
.count();
double rate = success * 100.0 / total;
System.out.printf(" 直塞总次数:%d,成功:%d,成功率:%.1f%%%n", total, success, rate);
}
}
运行结果
========== 总体直塞成功率 ==========
直塞总次数:11,成功:8,成功率:72.7%
========== 各球员直塞成功率 ==========
球员:德布劳内
直塞总次数:5,成功:3,成功率:60.0%
球员:B席
直塞总次数:3,成功:2,成功率:66.7%
球员:厄德高
直塞总次数:3,成功:3,成功率:100.0%
========== 各球队直塞成功率 ==========
球队:曼城
直塞总次数:8,成功:5,成功率:62.5%
球队:阿森纳
直塞总次数:3,成功:3,成功率:100.0%
========== 球员直塞成功率排名 ==========
厄德高:100.0%
B席:66.7%
德布劳内:60.0%
关键点说明
| 要点 | 说明 |
|---|---|
| 过滤条件 | passType == "THROUGH_BALL" 只统计直塞,排除普通传球 |
| 成功率公式 | 成功次数 / 总次数 × 100.0,注意用 0 避免整数除法 |
| 分组统计 | 用 Stream 的 groupingBy 按球员/球队分组 |
| 排名 | 用 sorted + Comparator 按成功率排序 |
可扩展方向
- 加入时间维度:按比赛、按上下半场统计
- 加入位置维度:前场直塞 vs 后场直塞成功率
- 威胁直塞:区分"形成射门的直塞"和"普通成功直塞"
- 数据来源:可对接 Opta、StatsBomb 等 API,替换模拟数据
- 输出报表:用 POI 导出 Excel,或画柱状图对比球员
如果需要对接真实数据源(如 CSV/JSON 文件读取)或加入可视化,告诉我,我可以继续扩展。