
在实际应用中,我们经常需要从一系列记录中筛选出每个分组在特定时间维度上的最新数据。例如,给定一个包含货币交易信息的列表,其中每条记录包含id、货币名称和最后接收时间戳,我们需要为每种货币的每一天,找出当天接收到的最新一条记录。
考虑以下 Currency 数据模型:
import java.time.LocalDateTime;
import java.util.Objects;
class Currency {
private Integer id;
private String name;
private LocalDateTime lastReceived;
public Currency(Integer id, String name, LocalDateTime lastReceived) {
this.id = id;
this.name = name;
this.lastReceived = lastReceived;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public LocalDateTime getLastReceived() {
return lastReceived;
}
@Override
public String toString() {
return "Currency{" +
"id=" + id +
", name='" + name + '\'' +
", lastReceived=" + lastReceived +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Currency currency = (Currency) o;
return Objects.equals(id, currency.id) && Objects.equals(name, currency.name) && Objects.equals(lastReceived, currency.lastReceived);
}
@Override
public int hashCode() {
return Objects.hash(id, name, lastReceived);
}
}假设我们有以下数据库记录:
| ID | NAME | LAST_RECEIVED |
|---|---|---|
| 1 | USD | 2022-05-18 09:04:01.545 |
| 2 | USD | 2022-05-18 08:04:01.545 |
| 3 | USD | 2022-05-19 08:04:01.545 |
| 4 | USD | 2022-05-20 08:04:01.545 |
| 5 | USD | 2022-05-20 11:04:01.545 |
| 6 | BUSD | 2022-05-18 08:04:01.545 |
我们期望的结果是针对每种货币的每一天,获取时间戳最新的记录:
| ID | NAME | LAST_RECEIVED |
|---|---|---|
| 1 | USD | 2022-05-18 09:04:01.545 |
| 3 | USD | 2022-05-19 08:04:01.545 |
| 5 | USD | 2022-05-20 11:04:01.545 |
| 6 | BUSD | 2022-05-18 08:04:01.545 |
Java 8 Stream API提供了强大的功能来处理集合数据。解决此问题的核心思路是:首先按货币名称和日期进行分组,然后在每个分组中找出 lastReceived 时间戳最大的那条记录。
立即学习“Java免费学习笔记(深入)”;
为了实现“按货币名称和日期分组”,我们可以创建一个复合键,将货币名称和 lastReceived 字段的日期部分(toLocalDate())组合起来。然后,使用 Collectors.groupingBy 进行分组,并结合 Collectors.collectingAndThen 与 Collectors.maxBy 来选择每个分组中时间戳最新的记录。
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class CurrencyProcessor {
public static void main(String[] args) {
List<Currency> data = Arrays.asList(
new Currency(1, "USD", LocalDateTime.parse("2022-05-18T09:04:01.545")),
new Currency(2, "USD", LocalDateTime.parse("2022-05-18T08:04:01.545")),
new Currency(3, "USD", LocalDateTime.parse("2022-05-19T08:04:01.545")),
new Currency(4, "USD", LocalDateTime.parse("2022-05-20T08:04:01.545")),
new Currency(5, "USD", LocalDateTime.parse("2022-05-20T11:04:01.545")),
new Currency(6, "BUSD", LocalDateTime.parse("2022-05-18T08:04:01.545"))
);
// 1. 获取所有货币类型在各日期的最新记录
List<Currency> lastByDateForAllCurrencies = new ArrayList<>(data
.stream()
.collect(Collectors.groupingBy(
// 复合键:货币名称 + 日期
curr -> Arrays.asList(curr.getName(), curr.getLastReceived().toLocalDate()),
// 在每个分组中找到lastReceived最大的记录
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Currency::getLastReceived)),
Optional::get // 将Optional<Currency>转换为Currency
)
))
.values()); // 获取Map中的所有值
System.out.println("所有货币类型在各日期的最新记录 (未排序):");
lastByDateForAllCurrencies.forEach(System.out::println);
// 如果需要排序,可以对结果列表进行排序
lastByDateForAllCurrencies.sort(
Comparator.comparing(Currency::getLastReceived)
.thenComparing(Currency::getName)
);
System.out.println("\n所有货币类型在各日期的最新记录 (已排序):");
lastByDateForAllCurrencies.forEach(System.out::println);
}
}代码解析:
如果只需要针对特定的货币(例如 "USD")执行此操作,可以在 stream() 之后添加一个 filter 操作。这样,分组键就可以简化为仅包含日期。
// 2. 获取特定货币类型(例如USD)在各日期的最新记录
String targetCurrency = "USD";
List<Currency> lastUSDByDate = new ArrayList<>(data
.stream()
.filter(curr -> targetCurrency.equalsIgnoreCase(curr.getName())) // 过滤特定货币
.collect(Collectors.groupingBy(
curr -> curr.getLastReceived().toLocalDate(), // 简化分组键为日期
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Currency::getLastReceived)),
Optional::get
)
))
.values());
// 对结果进行排序
lastUSDByDate.sort(Comparator.comparing(Currency::getLastReceived));
System.out.println("\n特定货币 (" + targetCurrency + ") 在各日期的最新记录:");
lastUSDByDate.forEach(System.out::println);代码解析:
对于存储在数据库中的数据,使用SQL查询通常是更高效和直接的方式。许多现代数据库(如PostgreSQL、MySQL 8+、Oracle SQL Server)都支持窗口函数,这使得按分组和排序获取最新/最旧记录变得非常简单。
以下是一个使用PostgreSQL的SQL窗口函数实现此逻辑的示例:
SELECT id, name, last_received
FROM (
SELECT c.*,
ROW_NUMBER() OVER (
PARTITION BY name, to_char(last_received, 'yyyy-MM-dd')
ORDER BY last_received DESC
) AS rn -- 为每个(name, date)分区内的记录按last_received降序编号
FROM Currency c
WHERE c.name = :currName -- 可选:如果只需要特定货币
) tbl
WHERE rn = 1 -- 选取每个分区中编号为1(即最新)的记录
ORDER BY last_received; -- 按日期排序最终结果SQL查询解析:
注意事项:
本教程提供了两种主要方法来解决按分组和日期获取最新记录的问题:
选择哪种方法取决于具体情况:
在实际开发中,理解这两种方法的原理和适用场景,能够帮助开发者选择最合适、最高效的解决方案。
以上就是使用Java 8 Stream API和SQL查询按日期分组获取最新记录教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号