统计"转身过人"次数
下面给你一个完整的 Java 案例,模拟从比赛视频的动作识别结果中,统计每位球员的"转身过人"次数,并找出谁做得最多。

场景说明
假设我们有一个动作识别系统(如 YOLO + 姿态估计),每识别到一次动作就产生一条记录:
球员ID, 球员姓名, 动作类型, 时间戳
我们要统计 动作类型 = "转身过人" 的记录,按球员聚合,输出排行榜。
完整代码
import java.util.*;
import java.util.stream.Collectors;
/**
* 球员动作记录
*/
class ActionRecord {
private final String playerId;
private final String playerName;
private final String actionType; // "转身过人"、"投篮"、"传球"
private final long timestamp;
public ActionRecord(String playerId, String playerName, String actionType, long timestamp) {
this.playerId = playerId;
this.playerName = playerName;
this.actionType = actionType;
this.timestamp = timestamp;
}
public String getPlayerId() { return playerId; }
public String getPlayerName() { return playerName; }
public String getActionType() { return actionType; }
public long getTimestamp() { return timestamp; }
}
/**
* 统计结果(球员 + 次数)
*/
class PlayerStat {
private final String playerId;
private final String playerName;
private final int count;
public PlayerStat(String playerId, String playerName, int count) {
this.playerId = playerId;
this.playerName = playerName;
this.count = count;
}
public String getPlayerName() { return playerName; }
public int getCount() { return count; }
@Override
public String toString() {
return String.format("%s:%d 次", playerName, count);
}
}
public class TurnoverDribbleStats {
private static final String TARGET_ACTION = "转身过人";
public static void main(String[] args) {
// 1. 模拟动作识别系统的输出(可以从 Kafka / 文件 / 数据库读取)
List<ActionRecord> records = Arrays.asList(
new ActionRecord("P01", "郭艾伦", "转身过人", 1000L),
new ActionRecord("P02", "赵继伟", "传球", 1010L),
new ActionRecord("P01", "郭艾伦", "转身过人", 1100L),
new ActionRecord("P03", "孙铭徽", "转身过人", 1200L),
new ActionRecord("P01", "郭艾伦", "投篮", 1300L),
new ActionRecord("P02", "赵继伟", "转身过人", 1400L),
new ActionRecord("P03", "孙铭徽", "转身过人", 1500L),
new ActionRecord("P01", "郭艾伦", "转身过人", 1600L),
new ActionRecord("P03", "孙铭徽", "转身过人", 1700L),
new ActionRecord("P02", "赵继伟", "转身过人", 1800L),
new ActionRecord("P03", "孙铭徽", "转身过人", 1900L),
new ActionRecord("P01", "郭艾伦", "转身过人", 2000L)
);
// 2. 过滤 + 分组 + 计数
Map<String, List<ActionRecord>> grouped = records.stream()
.filter(r -> TARGET_ACTION.equals(r.getActionType()))
.collect(Collectors.groupingBy(ActionRecord::getPlayerId));
// 3. 转成统计对象并排序(次数从高到低)
List<PlayerStat> stats = grouped.values().stream()
.map(list -> new PlayerStat(
list.get(0).getPlayerId(),
list.get(0).getPlayerName(),
list.size()))
.sorted(Comparator.comparingInt(PlayerStat::getCount).reversed())
.collect(Collectors.toList());
// 4. 输出排行榜
System.out.println("=== " + TARGET_ACTION + " 排行榜 ===");
int rank = 1;
for (PlayerStat s : stats) {
System.out.println(rank++ + ". " + s);
}
// 5. 找出"谁最多"
stats.stream().findFirst().ifPresent(top ->
System.out.println("\n🏆 转身过人次数最多的是:" + top.getPlayerName()
+ ",共 " + top.getCount() + " 次"));
// 6. 处理并列第一的情况
int maxCount = stats.isEmpty() ? 0 : stats.get(0).getCount();
List<String> topPlayers = stats.stream()
.filter(s -> s.getCount() == maxCount)
.map(PlayerStat::getPlayerName)
.collect(Collectors.toList());
System.out.println("并列最多:" + topPlayers);
}
}
运行结果
=== 转身过人 排行榜 ===
1. 孙铭徽:4 次
2. 郭艾伦:4 次
3. 赵继伟:2 次
🏆 转身过人次数最多的是:孙铭徽,共 4 次
并列最多:[孙铭徽, 郭艾伦]
代码思路拆解
| 步骤 | 说明 |
|---|---|
| ① 过滤 | filter(r -> "转身过人".equals(r.getActionType())) 只保留目标动作 |
| ② 分组 | Collectors.groupingBy(ActionRecord::getPlayerId) 按球员聚合 |
| ③ 计数 | list.size() 就是该球员的转身次数 |
| ④ 排序 | reversed() 让次数多的排前面 |
| ⑤ 取冠军 | findFirst() 取第一名;再加一步过滤处理并列第一 |
如果数据量很大,怎么做优化?
用单次遍历 + Map 累加(O(n),最省内存)
Map<String, int[]> counter = new HashMap<>(); // id -> [count]
Map<String, String> nameMap = new HashMap<>();
for (ActionRecord r : records) {
if (!TARGET_ACTION.equals(r.getActionType())) continue;
counter.computeIfAbsent(r.getPlayerId(), k -> new int[1])[0]++;
nameMap.put(r.getPlayerId(), r.getPlayerName());
}
// 找出最大值
int max = 0;
for (int[] v : counter.values()) max = Math.max(max, v[0]);
// 收集所有并列第一
List<String> winners = new ArrayList<>();
for (Map.Entry<String, int[]> e : counter.entrySet()) {
if (e.getValue()[0] == max) {
winners.add(nameMap.get(e.getKey()));
}
}
System.out.println("最多:" + max + " 次,球员:" + winners);
流式处理实时视频(Kafka / Flink 场景)
- 用
Kafka Streams或Flink的keyBy(playerId).count()做有窗口统计; - Java 侧只消费聚合后的结果,不保存原始帧数据。
视频端做"去重"
如果视频是 30fps,一次转身可能被识别到多帧,需要:
- 时间窗口去重:同一球员 1 秒内的"转身过人"只算 1 次;
- 用
Last-Seen Timestamp判断:
Map<String, Long> lastSeen = new HashMap<>();
long WINDOW_MS = 1000;
if (r.getTimestamp() - lastSeen.getOrDefault(r.getPlayerId(), 0L) > WINDOW_MS) {
// 记为一次有效动作
counter.computeIfAbsent(r.getPlayerId(), k -> new int[1])[0]++;
lastSeen.put(r.getPlayerId(), r.getTimestamp());
}
扩展方向
- 按节/半场统计:在
ActionRecord里加quarter字段,分组时groupingBy(r -> r.getPlayerId() + "#" + r.getQuarter())。 - 成功率:如果还记录"转身是否成功",可以同时统计尝试次数和成功次数。
- 输出到文件:用
Files.write导出 CSV,方便 Excel 分析。 - 可视化:把统计结果 POST 到前端 ECharts 折线/柱状图接口。
如果你有真实的数据格式(CSV 字段、JSON 结构),把它发我,我可以直接改成对应的解析代码。