如何基于日期字段对比两个对象列表并构建值映射
实现方案与代码
你的HashMap思路是可行的,不过可以通过引入中间存储类让逻辑更清晰,同时结合Java 8+的日期API提升代码健壮性。以下是具体实现和优化建议:
核心思路
- 定义中间类
DailyRateHolder,专门存储同一日期下listP和listQ的费率对象,便于后续对比处理 - 将两个列表的对象按日期分组,填充到中间映射结构中
- 遍历中间结构,按照你指定的规则选择最终的单日费率对象,生成目标映射
完整代码实现
首先补充原类A的getter方法(访问私有字段需要),然后实现处理逻辑:
import java.util.*; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; // 原费率对象类 class A { private Long rate1; private Long rate2; private Long timeCreated; // 秒级时间戳 private Long vCode; // 构造器 public A(Long rate1, Long rate2, Long timeCreated, Long vCode) { this.rate1 = rate1; this.rate2 = rate2; this.timeCreated = timeCreated; this.vCode = vCode; } // Getter方法 public Long getRate1() { return rate1; } public Long getRate2() { return rate2; } public Long getTimeCreated() { return timeCreated; } public Long getVCode() { return vCode; } } // 中间存储类:保存同一日期的双列表费率对象 class DailyRateHolder { private A pRate; private A qRate; public void setPRate(A pRate) { this.pRate = pRate; } public void setQRate(A qRate) { this.qRate = qRate; } public A getPRate() { return pRate; } public A getQRate() { return qRate; } } // 费率合并工具类 public class RateMerger { // 秒级时间戳转LocalDate(推荐用LocalDate替代Date,避免时区和可变问题) private static LocalDate convertToLocalDate(Long timestampSec) { if (timestampSec == null) { throw new IllegalArgumentException("时间戳不能为null"); } return Instant.ofEpochSecond(timestampSec) .atZone(ZoneId.systemDefault()) .toLocalDate(); } // 合并两个列表生成最终日期-费率映射 public static Map<LocalDate, A> mergeDailyRates(List<A> listP, List<A> listQ) { Map<LocalDate, DailyRateHolder> dateHolderMap = new HashMap<>(); // 处理listP for (A p : listP) { LocalDate date = convertToLocalDate(p.getTimeCreated()); DailyRateHolder holder = dateHolderMap.getOrDefault(date, new DailyRateHolder()); holder.setPRate(p); dateHolderMap.put(date, holder); } // 处理listQ for (A q : listQ) { LocalDate date = convertToLocalDate(q.getTimeCreated()); DailyRateHolder holder = dateHolderMap.getOrDefault(date, new DailyRateHolder()); holder.setQRate(q); dateHolderMap.put(date, holder); } // 按规则生成最终结果 Map<LocalDate, A> finalRateMap = new HashMap<>(); for (Map.Entry<LocalDate, DailyRateHolder> entry : dateHolderMap.entrySet()) { LocalDate date = entry.getKey(); DailyRateHolder holder = entry.getValue(); A finalRate; if (holder.getPRate() != null && holder.getQRate() != null) { // 替换为你的自定义对比规则,示例:取rate1更大的对象 finalRate = holder.getPRate().getRate1() >= holder.getQRate().getRate1() ? holder.getPRate() : holder.getQRate(); } else { // 仅存在其中一个列表的对象,直接使用 finalRate = Optional.ofNullable(holder.getPRate()).orElse(holder.getQRate()); } finalRateMap.put(date, finalRate); } return finalRateMap; } // 测试示例 public static void main(String[] args) { List<A> listP = Arrays.asList( new A(100L, 200L, 1698806400L, 2L), // 2023-11-01 new A(150L, 250L, 1698892800L, 2L) // 2023-11-02 ); List<A> listQ = Arrays.asList( new A(120L, 220L, 1698806400L, 3L), // 2023-11-01 new A(140L, 240L, 1698979200L, 3L) // 2023-11-03 ); Map<LocalDate, A> result = mergeDailyRates(listP, listQ); result.forEach((date, rate) -> { System.out.printf("日期:%s,rate1:%d,rate2:%d%n", date, rate.getRate1(), rate.getRate2()); }); } }
优化建议
- 优先使用LocalDate:传统
Date类是可变的,且包含时间部分,LocalDate专门用于表示日期,线程安全,完全贴合你的业务场景,避免不必要的时区问题。 - 简化分组逻辑(可选):如果偏好函数式编程,可以用Stream API替代循环分组,代码更简洁:
// Stream方式处理listP分组 Map<LocalDate, A> pMap = listP.stream() .collect(Collectors.toMap( a -> convertToLocalDate(a.getTimeCreated()), a -> a, (oldVal, newVal) -> oldVal // 同一日期仅一个对象,此逻辑不会触发 )); // listQ同理,后续合并两个map即可
- 抽取对比规则方法:如果你的费率对比规则复杂,建议单独抽取一个方法(比如
selectOptimalRate(A p, A q)),将规则逻辑与合并逻辑解耦,提升代码可维护性。 - 空值防御:在转换时间戳时加入非空判断,避免空指针异常;也可以用
Optional处理空对象,让代码更健壮。 - 初始化容量:由于列表最多100条数据,可以在创建HashMap时指定初始容量(比如
new HashMap<>(100)),减少扩容开销。
内容的提问来源于stack exchange,提问作者mysteryFruit
相关产品推荐
相关产品推荐

