开发者

Java中computeIfAbsent的功能和语法小结

目录
  • 1. 什么是computeIfAbsent?
  • 2.computeIfAbsent实战演示
    • 传统写法(手动null检查)
    • ✅computeIfAbsent简化写法
  • 3.comjsputeIfAbsentVS 传统方式:谁更强?
    • 4.computeIfAbsent适用场景
      • 5.computeIfAbsent性能测试 ️
        • 测试代码
        • 测试结果(不同环境略有差异)
      • 6. 可能的坑与注意点 ⚠️
        • 7. 结论

          在 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>&javascriptgt; 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);
             js         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的功能和语法小结的文章就介FBXfMl绍到这了,更多相关Java computeIfAbsent内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜