You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java 8 Filters、Lambda表达式按时间戳处理CSV记录(每分钟每客户限2条)

用Java 8过滤CSV记录:每分钟每个客户保留最多2条记录

这个需求很典型,咱们可以用Java 8的Stream API结合Lambda表达式来搞定,核心是要在过滤时跟踪每个客户每分钟的记录数——因为filter本身是无状态的,所以得用一个外部Map来存计数状态。下面一步步来实现:

首先,先定义一个数据类封装CSV里的每一条记录,方便后续处理:

import java.time.LocalDateTime;

public class Record {
    private final LocalDateTime timestamp;
    private final String client;

    public Record(LocalDateTime timestamp, String client) {
        this.timestamp = timestamp;
        this.client = client;
    }

    // 简单的getter方法
    public LocalDateTime getTimestamp() {
        return timestamp;
    }

    public String getClient() {
        return client;
    }

    @Override
    public String toString() {
        return timestamp + " " + client;
    }
}

接下来是核心的过滤逻辑。咱们需要一个Map来跟踪每个客户在每分钟内已保留的记录数,然后在Stream的filter方法里用Lambda表达式判断是否保留当前记录:

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

public class RecordProcessor {
    public static void main(String[] args) {
        // 模拟从CSV读取到的原始记录列表(实际项目里可以用OpenCSV等解析库读取)
        List<Record> rawRecords = Arrays.asList(
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "XYZ"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "ABC"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "LMN"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "ABC"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "ABC"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 0), "ABC"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 1), "ABC"),
                new Record(LocalDateTime.of(2018, 4, 22, 9, 2), "ABC")
        );

        // 单线程处理用HashMap即可,并行流处理请换成ConcurrentHashMap保证线程安全
        Map<String, Integer> minuteClientCounter = new HashMap<>();

        List<Record> filteredRecords = rawRecords.stream()
                .filter(record -> {
                    // 把时间戳截断到分钟级别,同一分钟的记录归为一组
                    LocalDateTime minuteLevelTime = record.getTimestamp()
                            .withSecond(0)
                            .withNano(0);
                    // 生成唯一跟踪键:分钟时间字符串 + 客户ID,确保每个客户每分钟计数独立
                    String trackingKey = minuteLevelTime.toString() + "_" + record.getClient();

                    // 获取当前已保留的记录数,默认0
                    int currentCount = minuteClientCounter.getOrDefault(trackingKey, 0);
                    if (currentCount < 2) {
                        // 计数加1,保留这条记录
                        minuteClientCounter.put(trackingKey, currentCount + 1);
                        return true;
                    }
                    // 超过2条,丢弃
                    return false;
                })
                .collect(Collectors.toList());

        // 输出验证结果
        System.out.println("过滤后的记录:");
        filteredRecords.forEach(System.out::println);
    }
}

关键逻辑说明:

  • 时间截断:用withSecond(0).withNano(0)把时间戳精确到分钟,让同一分钟内的所有记录归为同一个时间组
  • 计数跟踪:用Map的键区分不同客户在不同分钟的计数,确保每个客户每分钟的计数相互独立
  • 过滤判断:在Lambda表达式里检查当前计数是否小于2,满足条件就更新计数并保留记录,否则直接丢弃

额外注意事项:

  • 如果CSV里的时间是字符串格式(比如4/22/2018 9:00),需要先转换成LocalDateTime对象,可用DateTimeFormatter解析:
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy H:mm");
    LocalDateTime timestamp = LocalDateTime.parse("4/22/2018 9:00", formatter);
    
  • 如果用并行流处理(比如rawRecords.parallelStream()),一定要把HashMap换成ConcurrentHashMap,避免多线程下的计数混乱问题

内容的提问来源于stack exchange,提问作者tech_questions

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:55:46