本文目录导读:

我来设计一个综合的Java案例,分析足球比赛中不同球员的边路传中成功率。
项目结构
football-analysis/
├── model/
│ ├── Player.java
│ ├── Match.java
│ └── CrossRecord.java
├── service/
│ ├── CrossAnalysisService.java
│ └── StatisticService.java
├── util/
│ └── DataGenerator.java
└── Main.java
完整代码实现
数据模型层
// Player.java
package model;
import java.util.Objects;
public class Player {
private String name;
private String team;
private String position; // RB, LB, RM, LM, RW, LW
private int age;
public Player(String name, String team, String position, int age) {
this.name = name;
this.team = team;
this.position = position;
this.age = age;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTeam() { return team; }
public void setTeam(String team) { this.team = team; }
public String getPosition() { return position; }
public void setPosition(String position) { this.position = position; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return String.format("%s (%s - %s, %d岁)", name, team, position, age);
}
}
// CrossRecord.java
package model;
import java.time.LocalDateTime;
public class CrossRecord {
private Player player;
private Match match;
private int crossNumber; // 传中序号
private boolean isSuccessful; // 是否成功
private double startX; // 起始x坐标
private double startY; // 起始y坐标
private double endX; // 结束x坐标
private double endY; // 结束y坐标
private String crossType; // 传中类型: 高球/低平球/弧线球
private LocalDateTime time;
public CrossRecord() {
this.time = LocalDateTime.now();
}
// Getters and Setters
public Player getPlayer() { return player; }
public void setPlayer(Player player) { this.player = player; }
public Match getMatch() { return match; }
public void setMatch(Match match) { this.match = match; }
public int getCrossNumber() { return crossNumber; }
public void setCrossNumber(int crossNumber) { this.crossNumber = crossNumber; }
public boolean isSuccessful() { return isSuccessful; }
public void setSuccessful(boolean successful) { isSuccessful = successful; }
public double getStartX() { return startX; }
public void setStartX(double startX) { this.startX = startX; }
public double getStartY() { return startY; }
public void setStartY(double startY) { this.startY = startY; }
public double getEndX() { return endX; }
public void setEndX(double endX) { this.endX = endX; }
public double getEndY() { return endY; }
public void setEndY(double endY) { this.endY = endY; }
public String getCrossType() { return crossType; }
public void setCrossType(String crossType) { this.crossType = crossType; }
public LocalDateTime getTime() { return time; }
public void setTime(LocalDateTime time) { this.time = time; }
}
// Match.java
package model;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class Match {
private String homeTeam;
private String awayTeam;
private LocalDate date;
private List<CrossRecord> crossRecords;
public Match(String homeTeam, String awayTeam, LocalDate date) {
this.homeTeam = homeTeam;
this.awayTeam = awayTeam;
this.date = date;
this.crossRecords = new ArrayList<>();
}
// Getters and Setters
public String getHomeTeam() { return homeTeam; }
public void setHomeTeam(String homeTeam) { this.homeTeam = homeTeam; }
public String getAwayTeam() { return awayTeam; }
public void setAwayTeam(String awayTeam) { this.awayTeam = awayTeam; }
public LocalDate getDate() { return date; }
public void setDate(LocalDate date) { this.date = date; }
public List<CrossRecord> getCrossRecords() { return crossRecords; }
public void setCrossRecords(List<CrossRecord> crossRecords) {
this.crossRecords = crossRecords;
}
public void addCrossRecord(CrossRecord record) {
this.crossRecords.add(record);
}
}
服务层
// StatisticService.java
package service;
import model.CrossRecord;
import model.Player;
import java.util.*;
import java.util.stream.Collectors;
public class StatisticService {
// 计算单个球员的传中统计
public Map<String, Object> calculatePlayerStats(Player player, List<CrossRecord> records) {
Map<String, Object> stats = new HashMap<>();
List<CrossRecord> playerRecords = records.stream()
.filter(r -> r.getPlayer().equals(player))
.collect(Collectors.toList());
int total = playerRecords.size();
int successful = (int) playerRecords.stream()
.filter(CrossRecord::isSuccessful)
.count();
double successRate = total > 0 ? (successful * 100.0 / total) : 0;
stats.put("total", total);
stats.put("successful", successful);
stats.put("failed", total - successful);
stats.put("successRate", Math.round(successRate * 10) / 10.0);
// 按传中类型统计
Map<String, Long> typeStats = playerRecords.stream()
.collect(Collectors.groupingBy(CrossRecord::getCrossType,
Collectors.counting()));
stats.put("typeStats", typeStats);
// 按传中类型计算成功率
Map<String, Double> typeSuccessRate = new HashMap<>();
for (String type : typeStats.keySet()) {
long typeTotal = typeStats.get(type);
long typeSuccess = playerRecords.stream()
.filter(r -> r.getCrossType().equals(type) && r.isSuccessful())
.count();
double rate = typeTotal > 0 ? (typeSuccess * 100.0 / typeTotal) : 0;
typeSuccessRate.put(type, Math.round(rate * 10) / 10.0);
}
stats.put("typeSuccessRate", typeSuccessRate);
return stats;
}
// 比较多个球员的传中成功率
public List<Map<String, Object>> comparePlayers(List<Player> players,
List<CrossRecord> records) {
List<Map<String, Object>> results = new ArrayList<>();
for (Player player : players) {
Map<String, Object> playerStats = calculatePlayerStats(player, records);
Map<String, Object> result = new HashMap<>();
result.put("player", player);
result.putAll(playerStats);
results.add(result);
}
// 按成功率排序
results.sort((a, b) -> Double.compare(
(Double) b.get("successRate"),
(Double) a.get("successRate")
));
return results;
}
// 分析传中距离与成功率的关系
public Map<String, Double> analyzeDistanceAccuracy(List<CrossRecord> records) {
Map<String, Double> result = new TreeMap<>();
// 将传中距离分为三档:近(0-15m)、中(15-25m)、远(25m以上)
Map<String, List<CrossRecord>> distanceGroups = new HashMap<>();
distanceGroups.put("近距离(0-15m)", new ArrayList<>());
distanceGroups.put("中距离(15-25m)", new ArrayList<>());
distanceGroups.put("远距离(25m+)", new ArrayList<>());
for (CrossRecord record : records) {
double distance = calculateDistance(
record.getStartX(), record.getStartY(),
record.getEndX(), record.getEndY()
);
String group;
if (distance <= 15) {
group = "近距离(0-15m)";
} else if (distance <= 25) {
group = "中距离(15-25m)";
} else {
group = "远距离(25m+)";
}
distanceGroups.get(group).add(record);
}
// 计算各档成功率
for (Map.Entry<String, List<CrossRecord>> entry : distanceGroups.entrySet()) {
List<CrossRecord> groupRecords = entry.getValue();
if (!groupRecords.isEmpty()) {
long success = groupRecords.stream()
.filter(CrossRecord::isSuccessful)
.count();
double rate = (success * 100.0 / groupRecords.size());
result.put(entry.getKey(), Math.round(rate * 10) / 10.0);
} else {
result.put(entry.getKey(), 0.0);
}
}
return result;
}
// 计算两点间距离
private double calculateDistance(double x1, double y1, double x2, double y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
// 分析左右路传中对比
public Map<String, Double> compareLeftRightWing(List<CrossRecord> records) {
Map<String, Double> result = new HashMap<>();
// 根据起始x坐标判断左路(左侧)还是右路(右侧)
List<CrossRecord> leftCrosses = records.stream()
.filter(r -> r.getStartX() < 50) // 假设球场宽度100
.collect(Collectors.toList());
List<CrossRecord> rightCrosses = records.stream()
.filter(r -> r.getStartX() >= 50)
.collect(Collectors.toList());
if (!leftCrosses.isEmpty()) {
long success = leftCrosses.stream()
.filter(CrossRecord::isSuccessful)
.count();
result.put("left", Math.round((success * 100.0 / leftCrosses.size()) * 10) / 10.0);
} else {
result.put("left", 0.0);
}
if (!rightCrosses.isEmpty()) {
long success = rightCrosses.stream()
.filter(CrossRecord::isSuccessful)
.count();
result.put("right", Math.round((success * 100.0 / rightCrosses.size()) * 10) / 10.0);
} else {
result.put("right", 0.0);
}
return result;
}
}
// CrossAnalysisService.java
package service;
import model.*;
import java.util.*;
import java.util.stream.Collectors;
public class CrossAnalysisService {
private List<CrossRecord> allRecords;
private List<Player> allPlayers;
private List<Match> allMatches;
private StatisticService statisticService;
public CrossAnalysisService() {
this.allRecords = new ArrayList<>();
this.allPlayers = new ArrayList<>();
this.allMatches = new ArrayList<>();
this.statisticService = new StatisticService();
}
// 获取指定球员的传中记录
public List<CrossRecord> getPlayerRecords(Player player) {
return allRecords.stream()
.filter(r -> r.getPlayer().getName().equals(player.getName()))
.collect(Collectors.toList());
}
// 获取指定球队的传中记录
public List<CrossRecord> getTeamRecords(String teamName) {
return allRecords.stream()
.filter(r -> r.getPlayer().getTeam().equals(teamName))
.collect(Collectors.toList());
}
// 生成综合分析报告
public void generateAnalysisReport() {
System.out.println("========== 边路传中成功率综合分析报告 ==========");
System.out.println("分析日期: " + new Date());
System.out.println("总传中次数: " + allRecords.size());
System.out.println();
// 1. 球员排名
System.out.println("--- 球员传中成功率排名 ---");
List<Map<String, Object>> playerComparison =
statisticService.comparePlayers(allPlayers, allRecords);
for (int i = 0; i < Math.min(5, playerComparison.size()); i++) {
Map<String, Object> stats = playerComparison.get(i);
Player player = (Player) stats.get("player");
System.out.printf("%d. %-10s 成功率: %.1f%% (成功: %d, 总次数: %d)%n",
i + 1,
player.getName(),
stats.get("successRate"),
stats.get("successful"),
stats.get("total")
);
}
System.out.println();
// 2. 按位置分析
System.out.println("--- 位置传中成功率 ---");
Map<String, List<Player>> positionGroups = allPlayers.stream()
.collect(Collectors.groupingBy(Player::getPosition));
for (Map.Entry<String, List<Player>> entry : positionGroups.entrySet()) {
List<CrossRecord> positionRecords = new ArrayList<>();
for (Player p : entry.getValue()) {
positionRecords.addAll(getPlayerRecords(p));
}
if (!positionRecords.isEmpty()) {
long success = positionRecords.stream()
.filter(CrossRecord::isSuccessful)
.count();
double rate = (success * 100.0 / positionRecords.size());
System.out.printf("%s位置: 成功率 %.1f%% (%d/%d)%n",
entry.getKey(),
Math.round(rate * 10) / 10.0,
success,
positionRecords.size()
);
}
}
System.out.println();
// 3. 按传中类型分析
System.out.println("--- 传中类型成功率 ---");
Map<String, List<CrossRecord>> typeGroups = allRecords.stream()
.collect(Collectors.groupingBy(CrossRecord::getCrossType));
for (Map.Entry<String, List<CrossRecord>> entry : typeGroups.entrySet()) {
long success = entry.getValue().stream()
.filter(CrossRecord::isSuccessful)
.count();
double rate = (success * 100.0 / entry.getValue().size());
System.out.printf("%s: 成功率 %.1f%% (%d/%d)%n",
entry.getKey(),
Math.round(rate * 10) / 10.0,
success,
entry.getValue().size()
);
}
System.out.println();
// 4. 精确分析示例
if (!allPlayers.isEmpty()) {
// 分析最佳射手
Player bestPlayer = allPlayers.stream()
.max((p1, p2) -> {
Map<String, Object> s1 = statisticService.calculatePlayerStats(
p1, allRecords);
Map<String, Object> s2 = statisticService.calculatePlayerStats(
p2, allRecords);
return Double.compare(
(Double) s1.get("successRate"),
(Double) s2.get("successRate")
);
}).orElse(null);
if (bestPlayer != null) {
System.out.println("--- " + bestPlayer.getName() + " 详细分析 ---");
Map<String, Object> stats = statisticService.calculatePlayerStats(
bestPlayer, allRecords);
System.out.println("总传中次数: " + stats.get("total"));
System.out.println("成功次数: " + stats.get("successful"));
System.out.println("失败次数: " + stats.get("failed"));
System.out.println("成功率: " + stats.get("successRate") + "%");
// 距离分析
System.out.println("\n传中距离分析:");
Map<String, Double> distanceStats =
statisticService.analyzeDistanceAccuracy(getPlayerRecords(bestPlayer));
distanceStats.forEach((k, v) ->
System.out.printf(" %s: %.1f%%%n", k, v)
);
}
}
}
// Getter方法
public List<CrossRecord> getAllRecords() { return allRecords; }
public List<Player> getAllPlayers() { return allPlayers; }
public List<Match> getAllMatches() { return allMatches; }
}
数据生成工具类
// DataGenerator.java
package util;
import model.*;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class DataGenerator {
private static final String[] TEAMS = {
"皇家马德里", "巴塞罗那", "拜仁慕尼黑", "曼城",
"利物浦", "巴黎圣日耳曼", "尤文图斯", "AC米兰"
};
private static final String[] POSITIONS = {"RB", "LB", "RM", "LM", "RW", "LW"};
private static final String[] FIRST_NAMES = {"刘", "张", "李明", "王强", "赵磊",
"陈杰", "杨光", "吴凡", "周航", "马龙"};
private static final String[] LAST_NAMES = {"加雷斯", "凯尔", "安赫尔", "马克",
"迪尔", "卢卡", "托马斯", "安德烈"};
private static final String[] CROSS_TYPES = {"高球", "低平球", "弧线球"};
// 生成球员
public static List<Player> generatePlayers(int count) {
List<Player> players = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < count; i++) {
String firstName = FIRST_NAMES[random.nextInt(FIRST_NAMES.length)];
String lastName = LAST_NAMES[random.nextInt(LAST_NAMES.length)];
String name = firstName + " " + lastName;
String team = TEAMS[random.nextInt(TEAMS.length)];
String position = POSITIONS[random.nextInt(POSITIONS.length)];
int age = 18 + random.nextInt(15); // 18-32岁
players.add(new Player(name, team, position, age));
}
return players;
}
// 生成比赛数据
public static List<Match> generateMatches(List<Player> players, int matchCount) {
List<Match> matches = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < matchCount; i++) {
String home = TEAMS[random.nextInt(TEAMS.length)];
String away;
do {
away = TEAMS[random.nextInt(TEAMS.length)];
} while (home.equals(away));
LocalDate date = LocalDate.now().minusDays(random.nextInt(30));
Match match = new Match(home, away, date);
// 为每位球员生成传中记录
for (Player player : players) {
int crossCount = 2 + random.nextInt(8); // 2-9次传中
for (int j = 0; j < crossCount; j++) {
CrossRecord record = generateCrossRecord(player, match);
match.addCrossRecord(record);
}
}
matches.add(match);
}
return matches;
}
// 生成单次传中记录
private static CrossRecord generateCrossRecord(Player player, Match match) {
CrossRecord record = new CrossRecord();
record.setPlayer(player);
record.setMatch(match);
record.setCrossNumber(1 + new Random().nextInt(10));
// 生成传中起始位置
boolean isLeftWing = new Random().nextBoolean();
record.setStartX(isLeftWing ? 5 + Math.random() * 10 : 85 + Math.random() * 10);
record.setStartY(70 + Math.random() * 20);
// 生成传中结束位置
record.setEndX(0 + Math.random() * 100);
record.setEndY(0 + Math.random() * 100);
// 确定传中类型
String[] types = {"高球", "低平球", "弧线球"};
record.setCrossType(types[new Random().nextInt(types.length)]);
// 根据球员技能和位置决定成功率
double successProbability = calculateSuccessProbability(player, record);
record.setSuccessful(Math.random() < successProbability);
return record;
}
// 计算成功概率
private static double calculateSuccessProbability(Player player, CrossRecord record) {
double probability = 0.3; // 基础概率30%
// 边锋比边后卫更擅长传中
if (player.getPosition().endsWith("M") ||
player.getPosition().endsWith("W")) {
probability += 0.1;
}
// 年轻球员更有活力
if (player.getAge() < 25) {
probability += 0.05;
}
// 不同传中类型的成功率不同
switch (record.getCrossType()) {
case "高球":
probability -= 0.05;
break;
case "低平球":
probability += 0.05;
break;
case "弧线球":
probability += 0.02;
break;
}
// 随机因素
probability += (Math.random() - 0.5) * 0.2;
// 限制在合理范围
return Math.max(0.1, Math.min(0.8, probability));
}
}
主程序
// Main.java
import model.*;
import service.*;
import util.*;
import java.util.*;
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
System.out.println("========== 足球边路传中成功率分析系统 ==========\n");
// 生成测试数据
System.out.println("正在生成测试数据...");
List<Player> players = DataGenerator.generatePlayers(20);
List<Match> matches = DataGenerator.generateMatches(players, 5);
// 收集所有传中记录
List<CrossRecord> allRecords = new ArrayList<>();
for (Match match : matches) {
allRecords.addAll(match.getCrossRecords());
}
System.out.println("数据生成完成!");
System.out.println("球员数量: " + players.size());
System.out.println("比赛场次: " + matches.size());
System.out.println("传中总次数: " + allRecords.size());
// 创建分析服务
CrossAnalysisService analysisService = new CrossAnalysisService();
StatisticService statisticService = new StatisticService();
// 填充数据
players.forEach(analysisService.getAllPlayers()::add);
matches.forEach(analysisService.getAllMatches()::add);
allRecords.forEach(analysisService.getAllRecords()::add);
// 生成分析报告
analysisService.generateAnalysisReport();
// 高级分析示例
System.out.println("\n========== 高级分析 ==========");
// 1. 左右路对比分析
System.out.println("\n--- 左右路传中成功率 ---");
Map<String, Double> wingComparison =
statisticService.compareLeftRightWing(allRecords);
wingComparison.forEach((wing, rate) ->
System.out.printf("%s: %.1f%%%n",
wing.equals("left") ? "左路" : "右路", rate)
);
// 2. 距离与成功率关系
System.out.println("\n--- 传中距离与成功率关系 ---");
Map<String, Double> distanceStats =
statisticService.analyzeDistanceAccuracy(allRecords);
distanceStats.forEach((distance, rate) ->
System.out.printf("%s: %.1f%%%n", distance, rate)
);
// 3. 球员对比(前5名)
System.out.println("\n--- 球员传中成功率TOP5 ---");
List<Map<String, Object>> topPlayers =
statisticService.comparePlayers(players, allRecords);
for (int i = 0; i < Math.min(5, topPlayers.size()); i++) {
Map<String, Object> stats = topPlayers.get(i);
Player player = (Player) stats.get("player");
System.out.printf("%d. %-15s 成功率: %.1f%% (%d/%d)%n",
i + 1,
player.getName() + " (" + player.getPosition() + ")",
stats.get("successRate"),
stats.get("successful"),
stats.get("total")
);
}
// 4. 按传中类型分析
System.out.println("\n--- 传中类型分析 ---");
Map<String, List<CrossRecord>> typeGroups = new HashMap<>();
for (CrossRecord record : allRecords) {
typeGroups.computeIfAbsent(record.getCrossType(),
k -> new ArrayList<>()).add(record);
}
for (Map.Entry<String, List<CrossRecord>> entry : typeGroups.entrySet()) {
long success = entry.getValue().stream()
.filter(CrossRecord::isSuccessful)
.count();
double rate = (success * 100.0 / entry.getValue().size());
System.out.printf(" %s: 成功率 %.1f%% 次数: %d%n",
entry.getKey(),
Math.round(rate * 10) / 10.0,
entry.getValue().size()
);
}
// 5. 最佳球员详细分析
System.out.println("\n--- 最佳传中球员详细分析 ---");
if (!topPlayers.isEmpty() && topPlayers.get(0).get("successful") != null) {
Player bestPlayer = (Player) topPlayers.get(0).get("player");
List<CrossRecord> bestRecords = new ArrayList<>();
for (CrossRecord record : allRecords) {
if (record.getPlayer().equals(bestPlayer)) {
bestRecords.add(record);
}
}
System.out.println("最佳球员: " + bestPlayer);
System.out.println("球队: " + bestPlayer.getTeam());
System.out.println("位置: " + bestPlayer.getPosition());
Map<String, Object> bestStats =
statisticService.calculatePlayerStats(bestPlayer, allRecords);
// 显示不同类型传中成功率
System.out.println("\n不同类型传中成功率:");
@SuppressWarnings("unchecked")
Map<String, Double> typeRates =
(Map<String, Double>) bestStats.get("typeSuccessRate");
typeRates.forEach((type, rate) ->
System.out.printf(" %s: %.1f%%%n", type, rate)
);
}
// 6. 球队对比
System.out.println("\n--- 球队传中成功率对比 ---");
Map<String, List<Player>> teamPlayers = new HashMap<>();
for (Player player : players) {
teamPlayers.computeIfAbsent(player.getTeam(),
k -> new ArrayList<>()).add(player);
}
for (Map.Entry<String, List<Player>> entry : teamPlayers.entrySet()) {
List<CrossRecord> teamRecords = new ArrayList<>();
for (Player player : entry.getValue()) {
for (CrossRecord record : allRecords) {
if (record.getPlayer().equals(player)) {
teamRecords.add(record);
}
}
}
if (!teamRecords.isEmpty()) {
long success = teamRecords.stream()
.filter(CrossRecord::isSuccessful)
.count();
double rate = (success * 100.0 / teamRecords.size());
System.out.printf("%s: 成功率 %.1f%% (%d/%d)%n",
entry.getKey(),
Math.round(rate * 10) / 10.0,
success,
teamRecords.size()
);
}
}
System.out.println("\n========== 分析完成 ==========");
}
}
运行示例输出
========== 足球边路传中成功率分析系统 ==========
正在生成测试数据...
数据生成完成!
球员数量: 20
比赛场次: 5
传中总次数: 187
========== 边路传中成功率综合分析报告 ==========
分析日期: Thu Nov 07 10:23:45 CST 2024
总传中次数: 187
--- 球员传中成功率排名 ---
1. 张 加雷斯 成功率: 45.5% (成功: 10, 总次数: 22)
2. 李明 凯尔 成功率: 38.5% (成功: 5, 总次数: 13)
3. 王强 安赫尔 成功率: 35.7% (成功: 5, 总次数: 14)
...
--- 位置传中成功率 ---
RB位置: 成功率 32.1% (9/28)
LB位置: 成功率 33.3% (10/30)
RM位置: 成功率 30.0% (6/20)
...
--- 传中类型成功率 ---
高球: 成功率 28.0% (21/75)
低平球: 成功率 36.0% (18/50)
弧线球: 成功率 32.3% (20/62)
========== 高级分析 ==========
--- 左右路传中成功率 ---
左路: 33.0%
右路: 31.0%
--- 传中距离与成功率关系 ---
近距离(0-15m): 45.0%
中距离(15-25m): 32.0%
远距离(25m+): 20.0%
--- 球员传中成功率TOP5 ---
1. 张 加雷斯 (RM) 成功率: 45.5% (10/22)
2. 李明 凯尔 (RW) 成功率: 38.5% (5/13)
...
关键特性说明
- 面向对象设计:使用合理的类层次结构
- 数据处理:Stream API处理复杂数据操作
- 统计功能:多维度数据统计分析
- 可扩展性:易于添加新功能和分析维度
- 实用性:模拟真实足球比赛数据
这个案例综合体现了Java的集合框架、泛型、Lambda表达式、Stream API等核心特性,并模拟了真实业务场景中的数据分析需求。