computeIfAbsent是HashMap的原子操作方法,用于键不存在时计算并存入新值,实现懒加载;它非线程安全,多线程需用ConcurrentHashMap;mappingFunction不可返回null,且value为null视为不存在。

computeIfAbsent 是 HashMap 提供的一个非常实用的原子操作方法,用于“如果键不存在,就计算并放入一个新值”,常用来避免重复创建对象或防止并发下的重复初始化。
核心作用:懒加载 + 线程安全(单线程下)
它会先检查 key 是否存在且对应 value 不为 null;如果不存在(或值为 null),就调用你传入的 mappingFunction 计算出新值,并将 key → 新值 放入 map 中,然后返回这个新值;如果 key 已存在且 value 非 null,则直接返回已有值。
注意:它**不是线程安全的全局锁操作**,在多线程环境下仍可能被多个线程同时触发 mappingFunction(除非用 ConcurrentHashMap)。
基本用法示例
比如缓存用户订单列表,避免每次 get 都查数据库:
Map<Long, List<Order>> orderCache = new HashMap<>(); List<Order> orders = orderCache.computeIfAbsent(userId, id -> loadOrdersFromDB(id));
- 如果
userId在 map 中没对应值,就执行loadOrdersFromDB(id),结果存入 map 并返回 - 如果已存在(非 null),直接返回缓存的 list,不调用 lambda
- 若
loadOrdersFromDB返回 null,map 中该 key 的 value 就是 null —— 后续再调用还会重新计算,需注意处理
常见陷阱与注意事项
-
mappingFunction 不能返回 null:否则会抛
NullPointerException(JDK 8+ 行为) - 不要在 lambda 中修改 map 本身:比如在函数里再调用 computeIfAbsent,容易引发 ConcurrentModificationException 或死循环
-
value 为 null 时视为“不存在”:所以如果你允许 null 值,这个方法就不适合——考虑用
computeIfPresent或手动判断 - ConcurrentHashMap 更适合多线程场景:它的 computeIfAbsent 是加锁保证只执行一次 mappingFunction
替代写法对比(不推荐)
手动实现类似逻辑容易出错:
// ❌ 有竞态条件:两次 get,中间可能被其他线程插入
if (!map.containsKey(key)) {
map.put(key, expensiveCalculation());
}
// ❌ 无法避免多次计算(尤其多线程)
V v = map.get(key);
if (v == null) {
v = expensiveCalculation(); // 多个线程可能都执行这里
map.put(key, v);
}
而 computeIfAbsent 把“查 + 算 + 存 + 返”封装成一步,语义清晰、代码简洁、不易出错。


















