Java实现:筛选高分数据并检测2分钟内高频记录
街机游戏记录处理解决方案
需求回顾
- 仅筛选分数大于20的记录
- 将筛选后的记录按时间升序排序
- 识别满足以下条件的街机ID:
- 分数>20的记录总数超过5条
- 其中恰好有5条记录落在2分钟时间窗口内
- 输出符合条件的ID及该窗口内的最早时间
现有代码问题
你当前的代码存在几个关键问题:
- 未按街机ID分组,无法单独统计每个ID的记录
- 使用
Date[]数组处理数据不够灵活,且初始化、添加元素的方式有误 - 排序时机不对,应该在所有符合条件的记录收集完成后再排序
- 缺少核心的时间窗口判断逻辑
完整实现方案
1. 定义数据实体类
首先创建一个实体类封装单条记录,便于后续处理:
import java.time.LocalDateTime; public class ArcadeRecord { private String arcadeId; private LocalDateTime timestamp; private int score; public ArcadeRecord(String arcadeId, LocalDateTime timestamp, int score) { this.arcadeId = arcadeId; this.timestamp = timestamp; this.score = score; } // Getter方法 public String getArcadeId() { return arcadeId; } public LocalDateTime getTimestamp() { return timestamp; } public int getScore() { return score; } }
2. 核心处理逻辑
使用Java 8+的Stream API结合滑动窗口思想实现需求:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; public class ArcadeDataProcessor { // 时间格式化器,匹配输入的时间格式 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss.SSS"); // 2分钟对应的秒数,用于时间差判断 private static final long TWO_MINUTE_THRESHOLD = 120; public static void main(String[] args) { // 示例原始数据 String[] rawRecords = { "501,20220104 13:12:07.005,25", "501,20220104 13:12:07.002,25", "500,20220106 09:04:10.013,10", "501,20220104 13:12:07.001,25", "501,20220104 13:12:07.003,25", "501,20220104 13:12:07.004,25", "501,20220104 15:20:50.011,25" }; // 步骤1:解析原始数据,筛选分数>20的记录 List<ArcadeRecord> validRecords = Arrays.stream(rawRecords) .map(line -> { String[] parts = line.split(","); String id = parts[0]; LocalDateTime time = LocalDateTime.parse(parts[1], DATE_FORMATTER); int score = Integer.parseInt(parts[2]); return new ArcadeRecord(id, time, score); }) .filter(record -> record.getScore() > 20) .collect(Collectors.toList()); // 步骤2:按街机ID分组,每组内按时间升序排序 Map<String, List<ArcadeRecord>> groupedById = validRecords.stream() .collect(Collectors.groupingBy(ArcadeRecord::getArcadeId, Collectors.collectingAndThen(Collectors.toList(), list -> { list.sort(Comparator.comparing(ArcadeRecord::getTimestamp)); return list; }))); // 步骤3:检查每组是否符合条件 for (Map.Entry<String, List<ArcadeRecord>> entry : groupedById.entrySet()) { String arcadeId = entry.getKey(); List<ArcadeRecord> records = entry.getValue(); // 先判断总记录数是否超过5条 if (records.size() <= 5) { continue; } // 滑动窗口检查是否存在恰好5条记录在2分钟内 for (int i = 0; i <= records.size() - 5; i++) { ArcadeRecord windowStart = records.get(i); ArcadeRecord windowEnd = records.get(i + 4); long timeDiffSeconds = java.time.Duration.between(windowStart.getTimestamp(), windowEnd.getTimestamp()).getSeconds(); // 先判断当前窗口的5条记录是否在2分钟内 if (timeDiffSeconds <= TWO_MINUTE_THRESHOLD) { // 检查窗口外是否有其他记录也落在该窗口内,确保恰好5条 boolean hasExtraInWindow = false; // 检查窗口前一条记录(如果存在)是否在窗口内 if (i > 0) { long prevDiff = java.time.Duration.between(windowStart.getTimestamp(), records.get(i-1).getTimestamp()).getSeconds(); if (prevDiff >= -TWO_MINUTE_THRESHOLD) { hasExtraInWindow = true; } } // 检查窗口后一条记录(如果存在)是否在窗口内 if (i + 5 < records.size()) { long nextDiff = java.time.Duration.between(windowStart.getTimestamp(), records.get(i+5).getTimestamp()).getSeconds(); if (nextDiff <= TWO_MINUTE_THRESHOLD) { hasExtraInWindow = true; } } // 如果没有额外记录在窗口内,说明恰好5条符合条件 if (!hasExtraInWindow) { System.out.println("符合条件的街机ID:" + arcadeId); System.out.println("该时间范围最早时间:" + windowStart.getTimestamp().format(DATE_FORMATTER)); break; // 找到符合条件的窗口后跳出循环 } } } } } }
关键逻辑说明
- 使用
LocalDateTime替代旧的Date类,避免时区问题,时间操作更直观 - 按ID分组后再排序,确保每个ID的记录都是时间升序的,便于滑动窗口处理
- 滑动窗口思路:遍历所有可能的连续5条记录,计算首尾时间差是否≤2分钟
- 额外检查窗口前后的记录,确保该窗口内恰好只有这5条记录,满足需求中的"恰好5条"条件
- 输出结果直接打印符合条件的ID和窗口最早时间
内容的提问来源于stack exchange,提问作者PillagingProgrammer
相关产品推荐
相关产品推荐

