如何在嵌套HashMap中按日期条件删除父级条目?
问题描述
我有如下结构的嵌套HashMap:
HashMap<String, HashMap<String, String>> h = new HashMap<>(); HashMap<String, String> h1 = new HashMap<>(); h1.put("key10", "value10"); h1.put("key11", "value11"); h1.put("date", "2018-10-18T00:00:57.907Z"); h.put("1#100", h1); HashMap<String, String> h2 = new HashMap<>(); h2.put("key20", "value20"); h2.put("key21", "value21"); h2.put("date", "2023-02-03T10:00:00.907Z"); h.put("2#000", h2);
现在有大量类似条目,调度器触发时需要完成以下操作:
- 遍历所有嵌套的HashMap(用forEach/stream)
- 判断日期条件是否满足
- 找到对应的父级key并从主HashMap中删除该条目
示例中最终仅需保留2#000对应的条目:
h2.put("key20", "value20"); h2.put("key21", "value21"); h2.put("date", "2023-02-03T10:00:00.907Z"); h.put("2#000", h2); h2 => {key20 => value20, key21 => value21, date => 2023-02-03T10:00:00.907Z}
我目前写了部分代码:
h.forEach((k,v) -> { v.entrySet() .stream() .filter(e -> e.getKey().equals("date")) .filter(t -> Timestamp.from(Instant.now()).getTime() - Timestamp.valueOf(t.getValue()).getTime() > milisDiff) //需要通过key k访问父级并删除条目
请问能否通过Lambda一步实现,还是需要先收集父级key再执行删除操作?
解决方案
直接在forEach中删除的问题
不能直接在h.forEach(...)里调用h.remove(k),因为HashMap的forEach迭代器是快速失败的,迭代过程中修改集合结构(删除元素)会抛出ConcurrentModificationException。
两种可行方案
方案1:先收集需要删除的key,再批量删除
这是最稳妥的方式,先通过Stream筛选出所有符合删除条件的父级key,再统一删除:
import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; // 第一步:收集所有需要删除的父key Set<String> keysToRemove = h.entrySet().stream() .filter(entry -> { HashMap<String, String> nestedMap = entry.getValue(); String dateStr = nestedMap.get("date"); if (dateStr == null) { return false; // 没有date字段的不删除 } try { // 注意:Timestamp.valueOf不支持带Z的ISO格式,改用Instant解析 Instant dateInstant = Instant.parse(dateStr); long diff = System.currentTimeMillis() - dateInstant.toEpochMilli(); return diff > milisDiff; } catch (DateTimeParseException e) { return false; // 日期格式错误的不删除 } }) .map(Map.Entry::getKey) .collect(Collectors.toSet()); // 第二步:批量删除 keysToRemove.forEach(h::remove);
注意:原代码中
Timestamp.valueOf(t.getValue())会报错,因为2018-10-18T00:00:57.907Z是带时区的ISO格式,Timestamp.valueOf仅支持无时区的yyyy-MM-dd HH:mm:ss或yyyy-MM-dd'T'HH:mm:ss格式,需用Instant.parse()解析。
方案2:使用迭代器的remove方法(传统方式)
如果不想收集key,可以用HashMap的迭代器,在迭代过程中安全删除:
import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; Iterator<Map.Entry<String, HashMap<String, String>>> iterator = h.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, HashMap<String, String>> entry = iterator.next(); String dateStr = entry.getValue().get("date"); if (dateStr != null) { try { Instant dateInstant = Instant.parse(dateStr); long diff = System.currentTimeMillis() - dateInstant.toEpochMilli(); if (diff > milisDiff) { iterator.remove(); // 用迭代器的remove方法安全删除,不会触发并发修改异常 } } catch (DateTimeParseException e) { // 可添加日期格式错误的处理逻辑 } } }
为什么不能用Lambda一步删除?
因为Stream和forEach的迭代器不允许在迭代过程中修改原集合,强行调用h.remove(k)会触发并发修改异常。必须先收集待删除的key,或者使用支持安全删除的迭代器。
内容的提问来源于stack exchange,提问作者Lucho82
相关产品推荐
相关产品推荐

