
在数据处理场景中,我们经常会遇到需要从一系列记录中,根据某些条件(如实体名称、日期)筛选出最新的那条记录。例如,给定一个 currency 实体列表,其中包含货币名称(name)和接收时间(lastreceived),我们的目标是针对每种货币和每个日期,找出在该日期内接收时间最晚(最新)的那条记录。
考虑以下 Currency 类定义:
import java.time.LocalDateTime;
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;
}
// Getter方法
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 +
'}';
}
}假设我们有如下数据库记录示例:
| 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 |
我们期望的结果是针对每种货币的每个日期,只保留时间戳最新的记录。例如,对于 "USD" 货币:
| 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 |
Java 8引入的Stream API为处理集合数据提供了强大而灵活的工具。我们可以利用其分组、映射和聚合功能来实现上述需求。
立即学习“Java免费学习笔记(深入)”;
此方案适用于从包含多种货币的数据集中,为每种货币的每个日期获取最新记录。核心思路是创建一个复合键,包含货币名称和日期,然后根据这个复合键进行分组,并在每个组内找出 lastReceived 时间戳最大的记录。
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class CurrencyProcessor {
public static void main(String[] args) {
List<Currency> allCurrencies = 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")),
new Currency(7, "EUR", LocalDateTime.parse("2022-05-18T10:00:00.000")),
new Currency(8, "EUR", LocalDateTime.parse("2022-05-18T09:30:00.000"))
);
List<Currency> lastByDateForAllCurrencies = new ArrayList<>(allCurrencies
.stream()
.collect(Collectors.groupingBy(
// 复合键:货币名称 + 日期
curr -> Arrays.asList(curr.getName(), curr.getLastReceived().toLocalDate()),
// 在每个组内,找出lastReceived最大的Currency对象
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Currency::getLastReceived)),
Optional::get // 假设每个组至少有一个元素,因此Optional::get是安全的
)
))
.values()); // 获取Map中所有值(即筛选出的最新Currency对象)
System.out.println("--- 所有货币按日期筛选的最新记录 (未排序) ---");
lastByDateForAllCurrencies.forEach(System.out::println);
// 如果需要排序,可以对结果列表进行排序
lastByDateForAllCurrencies.sort(
Comparator.comparing(Currency::getName)
.thenComparing(Currency::getLastReceived)
);
System.out.println("\n--- 所有货币按日期筛选的最新记录 (已排序) ---");
lastByDateForAllCurrencies.forEach(System.out::println);
}
}代码解析:
如果我们的需求是只针对某一种特定货币(例如 "USD")进行处理,可以在分组前先进行过滤,这样可以简化分组键。
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class SpecificCurrencyProcessor {
public static void main(String[] args) {
List<Currency> allCurrencies = 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"))
);
String targetCurrencyName = "USD";
List<Currency> lastUSDByDate = new ArrayList<>(allCurrencies
.stream()
.filter(curr -> targetCurrencyName.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("--- USD货币按日期筛选的最新记录 ---");
lastUSDByDate.forEach(System.out::println);
}
}代码解析:
对于数据库中的大量数据,使用原生SQL查询通常是更高效的选择,尤其是在数据库层面支持窗口函数的情况下。窗口函数可以在不改变查询结果集行数的情况下,对分组内的数据进行计算。
这里以 PostgreSQL 为例,展示如何使用 ROW_NUMBER() 窗口函数实现相同的功能。
SELECT id, name, last_received
FROM (
SELECT
c.*,
-- 使用 ROW_NUMBER() 窗口函数为每个 (name, date) 分组内的记录编号
-- 按照 last_received 降序排列,最新记录的 rr 值为 1
ROW_NUMBER() OVER (
PARTITION BY name, to_char(last_received, 'yyyy-MM-dd')
ORDER BY last_received DESC
) AS rr
FROM Currency c
WHERE c.name = :currName -- 可选:如果只需要特定货币
) tbl
WHERE rr = 1 -- 筛选出每个分组中编号为 1 的记录(即最新记录)
ORDER BY last_received; -- 对最终结果按接收时间排序SQL查询解析:
JPA集成注意事项:
目前,JPA(Java Persistence API)标准本身对SQL窗口函数的支持并不完善。因此,如果需要使用窗口函数,通常需要通过 @Query(nativeQuery = true, value = "...") 注解来执行原生SQL查询。
// 假设这是你的JPA Repository接口
public interface CurrencyRepository extends JpaRepository<Currency, Integer> {
@Query(nativeQuery = true, value = """
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 rr
FROM Currency c
WHERE c.name = :currName
) tbl
WHERE rr = 1
ORDER BY last_received
""")
List<Currency> findLastByDateByCurrencyName(@Param("currName") String currName);
// 如果需要查询所有货币,可以移除 WHERE c.name = :currName
@Query(nativeQuery = true, value = """
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 rr
FROM Currency c
) tbl
WHERE rr = 1
ORDER BY name, last_received
""")
List<Currency> findAllLastByDate();
}Java 8 Stream API:
原生SQL查询(窗口函数):
在实际应用中,选择哪种方案取决于具体的数据量、性能要求以及应用架构。对于数据量较小且已在内存中的数据,Java Stream API是简洁高效的选择;而对于大规模数据库数据,原生SQL查询通常能提供更好的性能。
以上就是使用Java 8和SQL高效检索每日最新时间戳数据教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号