首页 > 解决方案 > Java 8 api 流。需要解决重复密钥问题

问题描述

我有一个如下所示的日志文件:

LSR2019-07-12_12:07:21.554
KMH2019-07-12_12:09:44.291
RGH2019-07-12_12:29:28.352
RGH2019-07-12_12:33:08.603

我有一个解析器将数据解析为缩写/日期/时间:

public Map <String, ?> parse() throws IOException {
        
        try (Stream<String>lines = Files.lines(path)){
            

        return lines.collect(Collectors.toMap(
                string -> string.substring(0,3),
                string -> new DateAndTimeInfo(LocalTime.from(DateTimeFormatter.ofPattern("HH:mm:ss.SSS").parse((string.substring(3).split("_")[1]))),
                        LocalDate.parse(string.substring(3).split("_")[0], DateTimeFormatter.ofPattern("yyyy-MM-dd"))),
                (string1, string2)-> ??? )); //Struggle here

解析后,它会创建一个映射,其中包含作为键的缩写和 DateAndTimeInfo 类的实例。该类如下所示:

public class DateAndTimeInfo {
    private List<LocalTime> localTime;
    private List<LocalDate> localDate;
    
    public DateAndTimeInfo(LocalTime localTime, LocalDate localDate) {
        this.localTime = Arrays.asList(localTime);
        this.localDate = Arrays.asList(localDate);
    }
    public List<LocalTime> getLocalTime() {
        return this.localTime;
    }
    public List<LocalDate> getLocalDate() {
        return this.localDate;
    }
    public void addAnotherLapTime(LocalTime localtime, LocalDate localDate) {
        this.localTime.add(localtime);
        this.localDate.add(localDate);
    }
}

一切正常,直到日志文件有重复的缩写。一旦出现重复键,我希望将数据存储在 DateAndTimeInfo 对象中,该对象是在解析第一个重复项时创建的。为此,我有addAnotherLapTime()方法。问题是我不知道如何在我的流中写它。

标签: javajava-stream

解决方案


由于值的组合最终会出现在List对象中,因此这是groupingBy收集器的任务。

但首先,你必须修复DateAndTimeInfo类。它只是构造函数

public DateAndTimeInfo(LocalTime localTime, LocalDate localDate) {
    this.localTime = Arrays.asList(localTime);
    this.localDate = Arrays.asList(localDate);
}

创建固定大小的列表,因此该方法

    public void addAnotherLapTime(LocalTime localtime, LocalDate localDate) {
        this.localTime.add(localtime);
        this.localDate.add(localDate);
    }

将失败并出现异常。

当你使用

public class DateAndTimeInfo {
    private List<LocalTime> localTime;
    private List<LocalDate> localDate;

    public DateAndTimeInfo() {
        localTime = new ArrayList<>();
        localDate = new ArrayList<>();
    }
    public List<LocalTime> getLocalTime() {
        return this.localTime;
    }
    public List<LocalDate> getLocalDate() {
        return this.localDate;
    }
    public void addAnotherLapTime(LocalTime localtime, LocalDate localDate) {
        this.localTime.add(localtime);
        this.localDate.add(localDate);
    }
}

相反,您可以像收集地图一样

public Map<String, DateAndTimeInfo> parse() throws IOException {
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss.SSS");
    try(Stream<String>lines = Files.lines(path)){
        return lines.collect(Collectors.groupingBy(
            string -> string.substring(0,3),
            Collector.of(DateAndTimeInfo::new, (info,str) -> {
                LocalDateTime ldt = LocalDateTime.parse(str.substring(3), f);
                info.addAnotherLapTime(ldt.toLocalTime(), ldt.toLocalDate());
            }, (info1,info2) -> {
                info1.getLocalDate().addAll(info2.getLocalDate());
                info1.getLocalTime().addAll(info2.getLocalTime());
                return info1;
        })));
    }
}

groupingBy允许为组指定一个收集器,此解决方案CollectorDateAndTimeInfo对象创建一个新的 ad-hoc。

您可以考虑是否真的要将日期和时间保留在不同的列表中。

替代方案是:

public class DateAndTimeInfo {
    private List<LocalDateTime> localDateTimes;

    public DateAndTimeInfo(List<LocalDateTime> list) {
        localDateTimes = list;
    }
    public List<LocalDateTime> getLocalDateTimes() {
        return localDateTimes;
    }
    // in case this is really needed
    public List<LocalTime> getLocalTime() {
        return localDateTimes.stream()
            .map(LocalDateTime::toLocalTime)
            .collect(Collectors.toList());
    }
    public List<LocalDate> getLocalDate() {
        return localDateTimes.stream()
            .map(LocalDateTime::toLocalDate)
            .collect(Collectors.toList());
    }
}

接着

public Map<String, DateAndTimeInfo> parse() throws IOException {
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss.SSS");
    try(Stream<String>lines = Files.lines(path)){
        return lines.collect(Collectors.groupingBy(
            string -> string.substring(0,3),
            Collectors.collectingAndThen(
                Collectors.mapping(s -> LocalDateTime.parse(s.substring(3), f),
                    Collectors.toList()),
                DateAndTimeInfo::new)));
    }
}

推荐阅读