
在实际业务场景中,我们经常需要处理包含时间戳的重复数据。例如,一个币种在同一天可能会有多次接收记录,但我们只关心当天最新的一条。我们的目标是,对于给定的币种(例如usd),在每一天中,只保留该币种在当天所有记录中lastreceived时间戳最新的那一条。
首先,定义数据模型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;
}
// Getter 方法
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public LocalDateTime getLastReceived() {
return lastReceived;
}
// 为了方便打印和比较,重写toString, equals, hashCode
@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.545899000 2 USD 2022-05-18 08:04:01.545899000 3 USD 2022-05-19 08:04:01.545899000 4 USD 2022-05-20 08:04:01.545899000 5 USD 2022-05-20 11:04:01.545899000 6 BUSD 2022-05-18 08:04:01.545899000
我们期望的结果是:
ID NAME LAST_RECEIVED -- ---- ------------- 1 USD 2022-05-18 09:04:01.545899000 (18号USD最新) 3 USD 2022-05-19 08:04:01.545899000 (19号USD最新) 5 USD 2022-05-20 11:04:01.545899000 (20号USD最新)
Java 8的Stream API提供了强大的数据处理能力,非常适合在内存中对集合进行分组、过滤和转换。
立即学习“Java免费学习笔记(深入)”;
要实现上述目标,我们需要执行以下步骤:
如果我们想获取所有币种在各自日期上的最新记录,可以使用Collectors.groupingBy结合Collectors.collectingAndThen和Collectors.maxBy。
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
public class CurrencyDataProcessor {
public static void main(String[] args) {
// 模拟数据
List<Currency> data = Arrays.asList(
new Currency(1, "USD", LocalDateTime.parse("2022-05-18 09:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))),
new Currency(2, "USD", LocalDateTime.parse("2022-05-18 08:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))),
new Currency(3, "USD", LocalDateTime.parse("2022-05-19 08:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))),
new Currency(4, "USD", LocalDateTime.parse("2022-05-20 08:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))),
new Currency(5, "USD", LocalDateTime.parse("2022-05-20 11:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))),
new Currency(6, "BUSD", LocalDateTime.parse("2022-05-18 08:04:01.545899", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS")))
);
// 获取所有币种在各日期的最新记录
List<Currency> lastByDateForAllCurrencies = new ArrayList<>(data
.stream() // Stream<Currency>
.collect(Collectors.groupingBy(
// 分组键:币种名称 + 日期 (LocalDateTime.toLocalDate())
curr -> Arrays.asList(curr.getName(), curr.getLastReceived().toLocalDate()),
Collectors.collectingAndThen(
// 在每个分组内,找到lastReceived最大的Currency对象
Collectors.maxBy(Comparator.comparing(Currency::getLastReceived)),
// Optional<Currency> -> Currency (Optional::get在确保存在值时是安全的)
Optional::get
)
)) // 结果是一个 Map<List<Object>, Currency>
.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")的数据,可以在分组前添加一个filter操作,并且分组键可以简化。
// 获取特定币种 (USD) 在各日期的最新记录
List<Currency> lastUSDByDate = new ArrayList<>(data
.stream()
.filter(curr -> "USD".equalsIgnoreCase(curr.getName())) // 过滤出USD币种
.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("\nUSD币种在各日期的最新记录:");
lastUSDByDate.forEach(System.out::println);代码解释:
对于大量数据,将数据处理逻辑下推到数据库通常是更高效的选择。虽然JPA等ORM框架对窗口函数支持有限,但我们可以使用原生SQL查询来实现。
SQL中的窗口函数(如ROW_NUMBER())非常适合这种“每组取N条”的需求。以下是一个针对PostgreSQL的示例:
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 -- 为每个分区内的记录分配行号,按last_received降序
FROM Currency c
WHERE c.name = :currName -- 可选:过滤特定币种
) tbl
WHERE rn = 1 -- 选取每个分区中行号为1的记录,即最新的一条
ORDER BY last_received; -- 对最终结果进行排序SQL查询解释:
如果使用Spring Data JPA,可以通过@Query注解结合nativeQuery = true来执行原生SQL查询。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
// 假设 CurrencyRepository 继承 JpaRepository
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 rn
FROM Currency c
WHERE c.name = :currName
) tbl
WHERE rn = 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 rn
FROM Currency c
) tbl
WHERE rn = 1
ORDER BY name, last_received
""")
List<Currency> findAllLastByDate();
}综上所述,根据具体的数据量、性能要求和现有技术栈,可以选择最适合的方案。对于中小型数据集,Java 8 Stream API提供了一种优雅且高效的内存处理方式;而对于大型数据集或需要利用数据库强大能力的场景,原生SQL查询(尤其是窗口函数)则是更专业的选择。
以上就是Java 8 Stream API与SQL:按日期和币种获取最新时间戳数据教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号