本文目录导读:

我来为您设计一个Java案例,用于统计足球比赛中高球传中争顶成功率。
整体设计思路
// 主要实体类 // 1. 球员类 (Player) // 2. 传中事件类 (CrossEvent) // 3. 争顶事件类 (AerialDuel) // 4. 统计服务类 (AerialDuelStatisticsService)
核心代码实现
基础实体类
import java.time.LocalDateTime;
import java.util.*;
// 球员类
class Player {
private String id;
private String name;
private String team;
private String position;
public Player(String id, String name, String team, String position) {
this.id = id;
this.name = name;
this.team = team;
this.position = position;
}
// Getters and Setters
public String getId() { return id; }
public String getName() { return name; }
public String getTeam() { return team; }
public String getPosition() { return position; }
}
// 传中事件类
class CrossEvent {
private String id;
private Player crosser; // 传中球员
private Player receiver; // 可能的接应球员
private double x; // 传中位置x坐标
private double y; // 传中位置y坐标
private LocalDateTime timestamp;
private String crossType; // 高球传中、低平球传中、45度传中等
private boolean isSuccessful; // 传中是否成功
public CrossEvent(String id, Player crosser, Player receiver,
double x, double y, String crossType) {
this.id = id;
this.crosser = crosser;
this.receiver = receiver;
this.x = x;
this.y = y;
this.timestamp = LocalDateTime.now();
this.crossType = crossType;
}
// Getters and Setters
public String getId() { return id; }
public Player getCrosser() { return crosser; }
public Player getReceiver() { return receiver; }
public double getX() { return x; }
public double getY() { return y; }
public String getCrossType() { return crossType; }
public boolean isSuccessful() { return isSuccessful; }
public void setSuccessful(boolean successful) { isSuccessful = successful; }
}
// 争顶事件类
class AerialDuel {
private String id;
private CrossEvent crossEvent;
private Player attacker; // 进攻球员(争顶球员)
private Player defender; // 防守球员
private boolean isWonByAttacker; // 进攻方是否赢得争顶
private boolean ledToShooting; // 争顶后是否形成射门
private double height; // 争顶高度
private LocalDateTime timestamp;
public AerialDuel(String id, CrossEvent crossEvent, Player attacker,
Player defender, double height) {
this.id = id;
this.crossEvent = crossEvent;
this.attacker = attacker;
this.defender = defender;
this.height = height;
this.timestamp = LocalDateTime.now();
}
// Getters and Setters
public String getId() { return id; }
public CrossEvent getCrossEvent() { return crossEvent; }
public Player getAttacker() { return attacker; }
public Player getDefender() { return defender; }
public boolean isWonByAttacker() { return isWonByAttacker; }
public void setWonByAttacker(boolean wonByAttacker) { isWonByAttacker = wonByAttacker; }
public boolean isLedToShooting() { return ledToShooting; }
public void setLedToShooting(boolean ledToShooting) { this.ledToShooting = ledToShooting; }
public double getHeight() { return height; }
}
统计服务类
class AerialDuelStatisticsService {
// 存储所有争顶事件
private List<AerialDuel> aerialDuels = new ArrayList<>();
// 添加争顶事件
public void addAerialDuel(AerialDuel duel) {
aerialDuels.add(duel);
}
// 计算整体争顶成功率
public double calculateOverallSuccessRate() {
if (aerialDuels.isEmpty()) {
return 0.0;
}
long successfulDuels = aerialDuels.stream()
.filter(AerialDuel::isWonByAttacker)
.count();
return (double) successfulDuels / aerialDuels.size() * 100;
}
// 按球队统计争顶成功率
public Map<String, Double> calcularTeamSuccessRate() {
Map<String, List<AerialDuel>> teamDuels = new HashMap<>();
for (AerialDuel duel : aerialDuels) {
String team = duel.getAttacker().getTeam();
teamDuels.computeIfAbsent(team, k -> new ArrayList<>()).add(duel);
}
Map<String, Double> result = new HashMap<>();
for (Map.Entry<String, List<AerialDuel>> entry : teamDuels.entrySet()) {
long total = entry.getValue().size();
long won = entry.getValue().stream()
.filter(AerialDuel::isWonByAttacker)
.count();
result.put(entry.getKey(), (double) won / total * 100);
}
return result;
}
// 按球员统计争顶成功率
public Map<String, Double> calculatePlayerSuccessRate() {
Map<String, List<AerialDuel>> playerDuels = new HashMap<>();
for (AerialDuel duel : aerialDuels) {
String playerId = duel.getAttacker().getId();
playerDuels.computeIfAbsent(playerId, k -> new ArrayList<>()).add(duel);
}
Map<String, Double> result = new HashMap<>();
for (Map.Entry<String, List<AerialDuel>> entry : playerDuels.entrySet()) {
long total = entry.getValue().size();
long won = entry.getValue().stream()
.filter(AerialDuel::isWonByAttacker)
.count();
result.put(entry.getKey(), (double) won / total * 100);
}
return result;
}
// 按传中类型统计争顶成功率
public Map<String, Double> calculateSuccessRateByCrossType() {
Map<String, List<AerialDuel>> crossTypeDuels = new HashMap<>();
for (AerialDuel duel : aerialDuels) {
String crossType = duel.getCrossEvent().getCrossType();
crossTypeDuels.computeIfAbsent(crossType, k -> new ArrayList<>()).add(duel);
}
Map<String, Double> result = new HashMap<>();
for (Map.Entry<String, List<AerialDuel>> entry : crossTypeDuels.entrySet()) {
long total = entry.getValue().size();
long won = entry.getValue().stream()
.filter(AerialDuel::isWonByAttacker)
.count();
result.put(entry.getKey(), (double) won / total * 100);
}
return result;
}
// 统计争顶后形成射门的效率
public double calculateShootingConversionRate() {
if (aerialDuels.isEmpty()) {
return 0.0;
}
long ledToShooting = aerialDuels.stream()
.filter(AerialDuel::isWonByAttacker)
.filter(AerialDuel::isLedToShooting)
.count();
return (double) ledToShooting / aerialDuels.size() * 100;
}
// 按球员位置统计争顶成功率
public Map<String, Double> calculateSuccessRateByPosition() {
Map<String, List<AerialDuel>> positionDuels = new HashMap<>();
for (AerialDuel duel : aerialDuels) {
String position = duel.getAttacker().getPosition();
positionDuels.computeIfAbsent(position, k -> new ArrayList<>()).add(duel);
}
Map<String, Double> result = new HashMap<>();
for (Map.Entry<String, List<AerialDuel>> entry : positionDuels.entrySet()) {
long total = entry.getValue().size();
long won = entry.getValue().stream()
.filter(AerialDuel::isWonByAttacker)
.count();
result.put(entry.getKey(), (double) won / total * 100);
}
return result;
}
// 综合统计报告
public void printDetailedStatistics() {
System.out.println("========== 高球传中争顶成功率统计报告 ==========");
System.out.println("1. 整体争顶成功率: " + String.format("%.1f%%", calculateOverallSuccessRate()));
System.out.println("2. 争顶后形成射门转化率: " + String.format("%.1f%%", calculateShootingConversionRate()));
System.out.println("\n3. 各球队争顶成功率:");
calcularTeamSuccessRate().forEach((team, rate) ->
System.out.println(" - " + team + ": " + String.format("%.1f%%", rate)));
System.out.println("\n4. 各球员争顶成功率:");
calculatePlayerSuccessRate().entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.forEach(entry ->
System.out.println(" - 球员ID " + entry.getKey() + ": " + String.format("%.1f%%", entry.getValue())));
System.out.println("\n5. 各传中类型争顶成功率:");
calculateSuccessRateByCrossType().forEach((type, rate) ->
System.out.println(" - " + type + ": " + String.format("%.1f%%", rate)));
System.out.println("\n6. 各位置球员争顶成功率:");
calculateSuccessRateByPosition().forEach((position, rate) ->
System.out.println(" - " + position + ": " + String.format("%.1f%%", rate)));
}
}
测试主程序
import java.util.Random;
public class FootballStatisticsDemo {
public static void main(String[] args) {
// 创建球员
List<Player> players = createPlayers();
// 创建统计服务
AerialDuelStatisticsService statisticsService = new AerialDuelStatisticsService();
// 模拟比赛数据
Random random = new Random(42); // 固定种子便于测试
// 模拟100次争顶事件
for (int i = 0; i < 100; i++) {
Player attacker = players.get(random.nextInt(players.size()));
Player defender = players.get(random.nextInt(players.size()));
Player crosser = players.get(random.nextInt(players.size()));
// 确保进攻和防守不是同一个球员
while (attacker.getId().equals(defender.getId())) {
defender = players.get(random.nextInt(players.size()));
}
// 创建传中事件
String[] crossTypes = {"高球", "低平球", "45度传中", "下底传中"};
CrossEvent crossEvent = new CrossEvent(
"C" + i,
crosser,
attacker,
random.nextDouble() * 100,
random.nextDouble() * 50,
crossTypes[random.nextInt(crossTypes.length)]
);
// 创建争顶事件
AerialDuel duel = new AerialDuel(
"AD" + i,
crossEvent,
attacker,
defender,
random.nextDouble() * 2 + 1.5 // 高度1.5-3.5米
);
// 随机设置争顶结果
duel.setWonByAttacker(random.nextBoolean());
duel.setLedToShooting(random.nextDouble() < 0.3); // 30%概率形成射门
// 传中成功与否与争顶结果相关
crossEvent.setSuccessful(duel.isWonByAttacker());
statisticsService.addAerialDuel(duel);
}
// 打印详细统计报告
statisticsService.printDetailedStatistics();
// 额外示例:不同位置的争顶成功率对比
System.out.println("\n========== 位置分析 ==========");
Map<String, Double> positionStats = statisticsService.calculateSuccessRateByPosition();
positionStats.entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.forEach(entry -> {
StringBuilder bar = new StringBuilder();
int barLength = (int) (entry.getValue() / 2);
for (int j = 0; j < barLength; j++) {
bar.append("█");
}
System.out.println(String.format("%-10s %5.1f%% %s",
entry.getKey(), entry.getValue(), bar.toString()));
});
}
// 创建示例球员
private static List<Player> createPlayers() {
List<Player> players = new ArrayList<>();
// 前锋
players.add(new Player("ST1", "罗纳尔多", "皇马", "前锋"));
players.add(new Player("ST2", "莱万", "巴萨", "前锋"));
players.add(new Player("ST3", "凯恩", "曼城", "前锋"));
// 中场
players.add(new Player("MID1", "德布劳内", "曼城", "中场"));
players.add(new Player("MID2", "克罗斯", "皇马", "中场"));
players.add(new Player("MID3", "B费", "曼联", "中场"));
// 后卫
players.add(new Player("DEF1", "范迪克", "利物浦", "后卫"));
players.add(new Player("DEF2", "拉莫斯", "巴黎", "后卫"));
players.add(new Player("DEF3", "鲁本·迪亚斯", "曼城", "后卫"));
return players;
}
}
核心功能特性
- 多维度统计:支持按球队、球员、位置、传中类型统计
- 转化率分析:分析争顶后形成射门的效率
- 可视化展示:使用条形图直观展示统计数据
- 数据模拟:提供随机数据供测试使用
扩展建议
- 添加时间序列分析:按时间段统计争顶成功率变化
- 对抗分析:分析特定球员之间的争顶对抗数据
- 空间分析:结合传中位置坐标进行热点分析
- 机器学习预测:基于历史数据预测争顶成功率
这个案例涵盖了高球传中争顶成功率统计的核心功能,可以根据实际需求进一步扩展和优化。