gpt4 book ai didi

Java 8过滤器考虑流的多个项目

转载 作者:行者123 更新时间:2023-12-01 07:46:28 25 4
gpt4 key购买 nike

考虑这个类:-

import java.sql.Timestamp;

public class Report {
private short value;
private Timestamp created;
//Getters, Setters
}

我有一个使用 ORDER BY 创建的 DESC 从数据库获取的报告列表

任务是获取每个月的最新报告。我知道它可以在 SQL 级别完成,但由于某种原因我需要在 Java 中完成。

这就是我解决这个问题的方法:-

    /**
* Assuming that the reports are sorted with <code>ORDER BY created DESC</code>, this method filters the list so
* that it contains only the latest report for any month.
*
* @param reports Sorted list of reports
* @return List containing not more than one report per month
*/
public static List<Report> oneReportPerMonthFilter(List<Report> reports) {
Map<String, Report> monthlyReports = new HashMap<>();
reports.forEach(report -> {
String yearMonth = getCreatedYearMonth(report);
if (!monthlyReports.containsKey(yearMonth)) {
monthlyReports.put(yearMonth, report);
}
});
return new ArrayList<>(monthlyReports.values());
}

private static String getCreatedYearMonth(Report report) {
return YearMonth
.from(ZonedDateTime.of(report.getCreated().toLocalDateTime(), ZoneOffset.UTC))
.toString();
}

问题1

虽然这按预期工作,但我必须创建一个Map,然后将转换回List。有没有更好的方法使用 Java 8 Stream API 来做到这一点?也许是一种更“实用”的方式?

问题2

能否简化将Timestamp转换为YearMonth的方法getCreatedYearMonth(Report report)?目前,它将 Timestamp 更改为 LocalDateTime,然后更改为 ZonedDateTime,然后更改为 YearMonth

单元测试:-

@Test
public void shouldFilterOutMultipleReportsPerMonth() {
Report report1 = new Report();
report1.setCreated(Timestamp.from(Instant.EPOCH));
report1.setValue((short) 100);

Report report2 = new Report();
report2.setCreated(Timestamp.from(Instant.EPOCH.plus(10, ChronoUnit.DAYS)));
report2.setValue((short) 200);

Report report3 = new Report();
report3.setCreated(Timestamp.from(Instant.EPOCH.plus(40, ChronoUnit.DAYS)));
report3.setValue((short) 300);

List<Report> reports = Stream.of(report3, report2, report1).collect(Collectors.toList());

List<Report> filteredReportList = ExampleClass.oneReportPerMonthFilter(reports);
Assert.assertEquals(2, filteredReportList.size());
Assert.assertEquals((short) 300, (short) filteredReportList.get(0).getValue());
Assert.assertEquals((short) 200, (short) filteredReportList.get(1).getValue());
}

编辑 1

回答

感谢大家的回答。使用 Amith 和 Johannes 的答案,我能够想出这个版本,它简单且易于阅读:-

public static List<Report> oneReportPerMonthFilter(List<Report> reports) {
Set<YearMonth> found = new HashSet<>();
return reports.stream()
.filter(r -> found.add(getCreatedYearMonth(r)))
.collect(Collectors.toList());
}

private static YearMonth getCreatedYearMonth(Report report) {
return YearMonth.from(
report.getCreated()
.toInstant()
.atZone(ZoneOffset.UTC));
}

似乎没有快速的方法将时间戳转换为年月。不过,我们可以从 Timestamp 中获取年月的字符串表示形式,如 Amith 所示。

最佳答案

您不应转换 YearMonth到一个字符串。只需省略 toString()部分。另外,我可以将其简化为:

private static YearMonth getCreatedYearMonth(Report report) {
return YearMonth.from(report.getCreated().toInstant().atZone(ZoneOffset.UTC));
}

要获得您想要的结果,您必须链接一些收集器:

Map<YearMonth, Report> last = reports.stream()
.collect(Collectors.groupingBy(r -> getCreatedYearMonth(r),
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Report::getCreated)),
Optional::get)));

外层CollectorgroupingBy :我们希望每个人都得到一些东西YearMonth 。下游收集器只会看到 Report同月。
下游CollectorcollectingAndThen ,因为maxBy收集器将产生 Optional<Report> 。但我们已经知道至少有一个 Report每个月,所以我们只需打开它。
最里面的收集器刚刚通过 Timestamp 获得最大值.

关于Java 8过滤器考虑流的多个项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50938904/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com