Java 案例:统计门前抢点射门次数对比
下面通过一个完整的 Java 案例,演示如何统计两名前锋"门前抢点"射门次数并进行对比,案例涵盖枚举、实体类、Stream 流统计、Comparator 排序、格式化输出等知识点。

需求说明
- 模拟一场足球比赛中两名前锋的射门记录
- 筛选出射门类型为"门前抢点"的记录
- 统计两人门前抢点射门次数并对比
- 输出对比结果(谁更多、差值、占比)
完整代码
import java.util.*;
import java.util.stream.Collectors;
// 1. 射门类型枚举
enum ShotType {
CLOSE_RANGE("门前抢点"),
LONG_SHOT("远射"),
HEADER("头球"),
PENALTY("点球"),
FREE_KICK("任意球");
private final String desc;
ShotType(String desc) { this.desc = desc; }
public String getDesc() { return desc; }
}
// 2. 射门记录实体
class ShotRecord {
private String playerName; // 球员名
private ShotType type; // 射门类型
private int minute; // 比赛分钟
private boolean onTarget; // 是否射正
public ShotRecord(String playerName, ShotType type, int minute, boolean onTarget) {
this.playerName = playerName;
this.type = type;
this.minute = minute;
this.onTarget = onTarget;
}
public String getPlayerName() { return playerName; }
public ShotType getType() { return type; }
public int getMinute() { return minute; }
public boolean isOnTarget() { return onTarget; }
@Override
public String toString() {
return String.format("%s 第%d分钟 %s%s",
playerName, minute, type.getDesc(), onTarget ? "(射正)" : "(射偏)");
}
}
// 3. 主程序
public class CloseRangeShotStats {
public static void main(String[] args) {
// 模拟射门数据
List<ShotRecord> shots = Arrays.asList(
new ShotRecord("张三", ShotType.CLOSE_RANGE, 12, true),
new ShotRecord("张三", ShotType.LONG_SHOT, 25, false),
new ShotRecord("张三", ShotType.CLOSE_RANGE, 38, true),
new ShotRecord("张三", ShotType.CLOSE_RANGE, 55, false),
new ShotRecord("张三", ShotType.HEADER, 70, true),
new ShotRecord("李四", ShotType.CLOSE_RANGE, 18, true),
new ShotRecord("李四", ShotType.CLOSE_RANGE, 33, true),
new ShotRecord("李四", ShotType.CLOSE_RANGE, 60, true),
new ShotRecord("李四", ShotType.CLOSE_RANGE, 82, false),
new ShotRecord("李四", ShotType.PENALTY, 45, true),
new ShotRecord("李四", ShotType.FREE_KICK, 88, false)
);
System.out.println("========== 全部射门记录 ==========");
shots.forEach(System.out::println);
// 4. 筛选"门前抢点"记录并按球员统计
Map<String, Long> closeRangeCount = shots.stream()
.filter(s -> s.getType() == ShotType.CLOSE_RANGE)
.collect(Collectors.groupingBy(
ShotRecord::getPlayerName,
Collectors.counting()
));
System.out.println("\n========== 门前抢点次数统计 ==========");
closeRangeCount.forEach((player, count) ->
System.out.printf("%s:%d 次%n", player, count));
// 5. 对比(按次数降序)
List<Map.Entry<String, Long>> ranking = closeRangeCount.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.collect(Collectors.toList());
System.out.println("\n========== 对比结果 ==========");
if (ranking.size() >= 2) {
Map.Entry<String, Long> first = ranking.get(0);
Map.Entry<String, Long> second = ranking.get(1);
long diff = first.getValue() - second.getValue();
double total = first.getValue() + second.getValue();
double ratio = total == 0 ? 0 : first.getValue() * 100.0 / total;
System.out.printf("🥇 %s:%d 次%n", first.getKey(), first.getValue());
System.out.printf("🥈 %s:%d 次%n", second.getKey(), second.getValue());
System.out.printf("次数差:%d 次%n", diff);
System.out.printf("%s 门前抢点占比:%.2f%%%n", first.getKey(), ratio);
if (diff == 0) {
System.out.println("两人门前抢点能力不分伯仲!");
} else {
System.out.printf("%s 门前抢点次数多 %d 次,抢点意识更强。%n",
first.getKey(), diff);
}
}
// 6. 附加:门前抢点射正率对比
System.out.println("\n========== 门前抢点射正率对比 ==========");
Map<String, List<ShotRecord>> grouped = shots.stream()
.filter(s -> s.getType() == ShotType.CLOSE_RANGE)
.collect(Collectors.groupingBy(ShotRecord::getPlayerName));
grouped.forEach((player, list) -> {
long onTarget = list.stream().filter(ShotRecord::isOnTarget).count();
double rate = list.isEmpty() ? 0 : onTarget * 100.0 / list.size();
System.out.printf("%s:射门 %d 次,射正 %d 次,射正率 %.2f%%%n",
player, list.size(), onTarget, rate);
});
}
}
运行结果
========== 全部射门记录 ==========
张三 第12分钟 门前抢点(射正)
张三 第25分钟 远射(射偏)
张三 第38分钟 门前抢点(射正)
张三 第55分钟 门前抢点(射偏)
张三 第70分钟 头球(射正)
李四 第18分钟 门前抢点(射正)
李四 第33分钟 门前抢点(射正)
李四 第60分钟 门前抢点(射正)
李四 第82分钟 门前抢点(射偏)
李四 第45分钟 点球(射正)
李四 第88分钟 任意球(射偏)
========== 门前抢点次数统计 ==========
张三:3 次
李四:4 次
========== 对比结果 ==========
🥇 李四:4 次
🥈 张三:3 次
次数差:1 次
李四 门前抢点占比:57.14%
李四 门前抢点次数多 1 次,抢点意识更强。
========== 门前抢点射正率对比 ==========
张三:射门 3 次,射正 2 次,射正率 66.67%
李四:射门 4 次,射正 3 次,射正率 75.00%
核心知识点解析
| 技术点 | 说明 |
|---|---|
枚举 ShotType |
用枚举代替字符串表示射门类型,避免魔法值,便于扩展 |
filter |
过滤出 type == CLOSE_RANGE 的记录 |
Collectors.groupingBy |
按球员分组,便于分人统计 |
Collectors.counting() |
作为下游收集器,统计每组记录数 |
comparingByValue().reversed() |
按次数降序排序,得到排名 |
Map.Entry |
遍历 Map 键值对,取第一名/第二名进行对比 |
| 附加射正率 | 再次分组并 filter(isOnTarget) 计算命中率,扩展对比维度 |
扩展思路
- 多轮比赛累积:把
shots换成从数据库/文件读取的List,可统计联赛整个赛季。 - 加入队伍维度:在
ShotRecord中加team字段,groupingBy(team → player)多级分组。 - 图表输出:统计结果导出为 CSV,用 Excel/Python 画柱状图对比。
- 自定义对比指标:可把"次数"换成"预期进球(xG)""转化率"等指标,只需改
counting()为summingDouble。
如果需要,我可以把上面的例子改成 多级分组(球队 → 球员)+ 输出到文件 的版本。
版权声明:本站文章为原创整理,转载请注明出处。