You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 14:40:23