Java中computeIfAbsent的功能和语法小结

 更新时间:2025年10月27日 09:19:27   作者:总是学不会.  
Java8的computeIfAbsent方法简化了从Map中获取值并处理默认值的过程,减少了手动null检查的繁琐,提高了代码的简洁性和可读性,本文就来具体了解一下computeIfAbsent的使用,感兴趣的可以了解一下

在 Java 开发中,我们经常需要从 Map 获取一个值,如果这个值不存在,就先创建一个默认值再存进去。这通常需要手动 null 检查,但 Java 8 引入的 computeIfAbsent 方法可以让代码更简洁、更高效!

今天,我们就来深挖 computeIfAbsent,看看它的作用、用法、对比、适用场景,并通过代码示例和性能分析让你真正掌握它!💡

1. 什么是computeIfAbsent? 🧐

computeIfAbsent 是 Java 8 在 Map 接口中新增的方法,作用是:

  • 如果 key 存在,直接返回对应的 value。
  • 如果 key 不存在,则使用给定的函数计算一个新值,放入 Map,然后返回这个值。

方法签名

V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

🔹 参数解析

  • key:需要查询的键。
  • mappingFunction:当 key 不存在时,计算默认值的函数。

2.computeIfAbsent实战演示 🎬

🌟 传统写法(手动null检查)

Map<String, List<String>> map = new HashMap<>();
String key = "fruits";

List<String> list = map.get(key);
if (list == null) {
    list = new ArrayList<>();
    map.put(key, list);
}
list.add("apple");

问题

  1. 需要先 get,再 null 检查,最后 put,代码显得繁琐。
  2. 如果代码写多了,容易出现漏 put 的情况,导致 NullPointerException

✅computeIfAbsent简化写法

Map<String, List<String>> map = new HashMap<>();
String key = "fruits";

map.computeIfAbsent(key, k -> new ArrayList<>()).add("apple");

优势: ✔ 代码更简洁:不用手动 null 检查,避免冗余代码。
避免并发问题:在多线程环境下,computeIfAbsent 可以减少竞态条件(race condition)。
更直观:代码的意图更加清晰,增强可读性。

3.computeIfAbsentVS 传统方式:谁更强? 🤔

对比项手动 null 检查computeIfAbsent
代码简洁度代码较冗长,需多步操作一行代码即可完成
可读性if 逻辑较多,显得繁琐逻辑清晰,一目了然
性能get -> null 检查 -> put内部优化后效率更高
线程安全需手动同步,易有竞态问题更安全,减少并发问题

结论

  • 大多数情况下computeIfAbsent 是更好的选择,它代码简洁且高效。
  • 但如果初始化逻辑特别复杂,比如涉及多个操作,手动 null 检查可能更灵活。

4.computeIfAbsent适用场景 🔥

🚀 缓存机制
当我们需要从缓存(如 MapConcurrentHashMap)获取数据,若数据不存在,则动态计算并存入缓存。

Map<String, String> cache = new HashMap<>();
String result = cache.computeIfAbsent("key", k -> loadFromDatabase(k));

👆 loadFromDatabase(k) 只有在 key 不存在时才会执行。

🚀 多值存储(如 Map<String, List<T>>)

Map<String, Set<String>> categoryMap = new HashMap<>();
categoryMap.computeIfAbsent("fruits", k -> new HashSet<>()).add("apple");

👆 这样就能确保 Set 一定存在,然后直接 add 元素,避免 NullPointerException

🚀 统计计数

Map<String, Integer> wordCount = new HashMap<>();
wordCount.computeIfAbsent("hello", k -> 0);
wordCount.put("hello", wordCount.get("hello") + 1);

👆 这样可以避免手动初始化 0,提高代码整洁度。

5.computeIfAbsent性能测试 🏎️

让我们测试两种方式的性能,看看 computeIfAbsent 是否真的更快!💡

测试代码

import java.util.*;

public class ComputeIfAbsentBenchmark {
    public static void main(String[] args) {
        Map<Integer, Set<Integer>> map1 = new HashMap<>();
        Map<Integer, Set<Integer>> map2 = new HashMap<>();
        int iterations = 1_000_000;

        // 使用 computeIfAbsent
        long start1 = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            map1.computeIfAbsent(i % 100, k -> new HashSet<>()).add(i);
        }
        long end1 = System.nanoTime();
        System.out.println("Using computeIfAbsent: " + (end1 - start1) / 1_000_000 + " ms");

        // 传统方式
        long start2 = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            Set<Integer> set = map2.get(i % 100);
            if (set == null) {
                set = new HashSet<>();
                map2.put(i % 100, set);
            }
            set.add(i);
        }
        long end2 = System.nanoTime();
        System.out.println("Using manual null check: " + (end2 - start2) / 1_000_000 + " ms");
    }
}

测试结果(不同环境略有差异)

Using computeIfAbsent: 120 ms
Using manual null check: 135 ms

computeIfAbsent 在大多数情况下略快一些,特别是在高并发环境下。

6. 可能的坑与注意点 ⚠️

避免副作用

map.computeIfAbsent("key", k -> {
    System.out.println("计算新值");
    return "value";
});

👆 即使 key 存在,方法依然可能会调用 Function,但不会存储新值。因此,要确保 Function 不带有副作用!

线程安全 HashMap 不是线程安全的,若要并发使用 computeIfAbsent,请使用 ConcurrentHashMap

7. 结论 🎯

computeIfAbsentMap 操作更加简洁、高效、优雅。
✅ 适用于 缓存机制集合初始化计数 等场景。
✅ 在 多线程环境下,结合 ConcurrentHashMap,避免竞态条件。
✅ 性能通常比手动 null 检查略优,但如果初始化逻辑复杂,手动 null 检查可能更灵活。

到此这篇关于Java中computeIfAbsent的功能和语法小结的文章就介绍到这了,更多相关Java computeIfAbsent内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot实现数据源动态切换的最佳姿势

    SpringBoot实现数据源动态切换的最佳姿势

    这篇文章主要为大家详细介绍一下SpringBoot实现数据源动态切换的最佳姿势,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03
  • 实战分布式医疗挂号系统登录接口整合阿里云短信详情

    实战分布式医疗挂号系统登录接口整合阿里云短信详情

    这篇文章主要为大家介绍了实战分布式医疗挂号系统登录接口整合阿里云短信详情,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2022-04-04
  • Spring Boot 通过AOP和自定义注解实现权限控制的方法

    Spring Boot 通过AOP和自定义注解实现权限控制的方法

    这篇文章主要介绍了Spring Boot 通过AOP和自定义注解实现权限控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Java基于ProcessBuilder+FFmpeg实现MP4转WAV音频转码方案

    Java基于ProcessBuilder+FFmpeg实现MP4转WAV音频转码方案

    在音视频处理场景中,将MP4视频文件中的音频流提取为WAV格式是一个常见需求,本文介绍一种基于Java ProcessBuilder + FFmpeg的高效解决方案,通过调用命令行工具FFmpeg完成转换,实现跨平台、高质量的音频提取,需要的朋友可以参考下
    2026-03-03
  • 关于JDK8中的字符串拼接示例详解

    关于JDK8中的字符串拼接示例详解

    字符串拼接问题应该是每个Java程序员都熟知的事情了,几乎每个Java程序员都读过关于StringBuffer/StringBuilder来拼接字符串。下面这篇文章主要给大家介绍了关于JDK8中的字符串拼接的相关资料,需要的朋友可以参考下。
    2018-04-04
  • java项目中classpath的理解

    java项目中classpath的理解

    这篇文章介绍了java项目中classpath的理解,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • cookie、session和java过滤器结合实现登陆程序

    cookie、session和java过滤器结合实现登陆程序

    这篇文章主要为大家详细介绍了cookie、session和java过滤器结合实现登陆程序的具体代码,感兴趣的朋友可以参考一下
    2016-05-05
  • Springboot2.0自适应效果错误响应过程解析

    Springboot2.0自适应效果错误响应过程解析

    这篇文章主要介绍了Springboot2.0自适应效果错误响应过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java类加载机制实现步骤解析

    Java类加载机制实现步骤解析

    这篇文章主要介绍了Java类加载机制实现步骤解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • MyBatis插入时获取自增主键方法

    MyBatis插入时获取自增主键方法

    MyBatis 3.2.6插入时候获取自增主键方法有两种,下面以以MySQL5.5为例通过两种方法给大家介绍mybatis获取自增主键的方法,一起看看吧
    2016-11-11

最新评论