Java中computeIfAbsent的功能和語法小結(jié)
在 Java 開發(fā)中,我們經(jīng)常需要從 Map 獲取一個(gè)值,如果這個(gè)值不存在,就先創(chuàng)建一個(gè)默認(rèn)值再存進(jìn)去。這通常需要手動(dòng) null 檢查,但 Java 8 引入的 computeIfAbsent 方法可以讓代碼更簡潔、更高效!
今天,我們就來深挖 computeIfAbsent,看看它的作用、用法、對比、適用場景,并通過代碼示例和性能分析讓你真正掌握它!??
1. 什么是computeIfAbsent? ??
computeIfAbsent 是 Java 8 在 Map 接口中新增的方法,作用是:
- 如果 key 存在,直接返回對應(yīng)的 value。
- 如果 key 不存在,則使用給定的函數(shù)計(jì)算一個(gè)新值,放入
Map,然后返回這個(gè)值。
方法簽名:
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
?? 參數(shù)解析:
key:需要查詢的鍵。mappingFunction:當(dāng)key不存在時(shí),計(jì)算默認(rèn)值的函數(shù)。
2.computeIfAbsent實(shí)戰(zhàn)演示 ??
?? 傳統(tǒng)寫法(手動(dòng)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");
問題:
- 需要先
get,再null檢查,最后put,代碼顯得繁瑣。 - 如果代碼寫多了,容易出現(xiàn)漏
put的情況,導(dǎo)致NullPointerException。
?computeIfAbsent簡化寫法
Map<String, List<String>> map = new HashMap<>();
String key = "fruits";
map.computeIfAbsent(key, k -> new ArrayList<>()).add("apple");
優(yōu)勢: ? 代碼更簡潔:不用手動(dòng) null 檢查,避免冗余代碼。
? 避免并發(fā)問題:在多線程環(huán)境下,computeIfAbsent 可以減少競態(tài)條件(race condition)。
? 更直觀:代碼的意圖更加清晰,增強(qiáng)可讀性。
3.computeIfAbsentVS 傳統(tǒng)方式:誰更強(qiáng)? ??
| 對比項(xiàng) | 手動(dòng) null 檢查 | computeIfAbsent |
|---|---|---|
| 代碼簡潔度 | 代碼較冗長,需多步操作 | 一行代碼即可完成 |
| 可讀性 | if 邏輯較多,顯得繁瑣 | 邏輯清晰,一目了然 |
| 性能 | get -> null 檢查 -> put | 內(nèi)部優(yōu)化后效率更高 |
| 線程安全 | 需手動(dòng)同步,易有競態(tài)問題 | 更安全,減少并發(fā)問題 |
結(jié)論:
- 大多數(shù)情況下,
computeIfAbsent是更好的選擇,它代碼簡潔且高效。 - 但如果初始化邏輯特別復(fù)雜,比如涉及多個(gè)操作,手動(dòng)
null檢查可能更靈活。
4.computeIfAbsent適用場景 ??
?? 緩存機(jī)制:
當(dāng)我們需要從緩存(如 Map 或 ConcurrentHashMap)獲取數(shù)據(jù),若數(shù)據(jù)不存在,則動(dòng)態(tài)計(jì)算并存入緩存。
Map<String, String> cache = new HashMap<>();
String result = cache.computeIfAbsent("key", k -> loadFromDatabase(k));
?? loadFromDatabase(k) 只有在 key 不存在時(shí)才會(huì)執(zhí)行。
?? 多值存儲(chǔ)(如 Map<String, List<T>>)
Map<String, Set<String>> categoryMap = new HashMap<>();
categoryMap.computeIfAbsent("fruits", k -> new HashSet<>()).add("apple");
?? 這樣就能確保 Set 一定存在,然后直接 add 元素,避免 NullPointerException。
?? 統(tǒng)計(jì)計(jì)數(shù)
Map<String, Integer> wordCount = new HashMap<>();
wordCount.computeIfAbsent("hello", k -> 0);
wordCount.put("hello", wordCount.get("hello") + 1);
?? 這樣可以避免手動(dòng)初始化 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");
// 傳統(tǒng)方式
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");
}
}
測試結(jié)果(不同環(huán)境略有差異)
Using computeIfAbsent: 120 ms
Using manual null check: 135 ms
? computeIfAbsent 在大多數(shù)情況下略快一些,特別是在高并發(fā)環(huán)境下。
6. 可能的坑與注意點(diǎn) ??
? 避免副作用
map.computeIfAbsent("key", k -> {
System.out.println("計(jì)算新值");
return "value";
});
?? 即使 key 存在,方法依然可能會(huì)調(diào)用 Function,但不會(huì)存儲(chǔ)新值。因此,要確保 Function 不帶有副作用!
? 線程安全 HashMap 不是線程安全的,若要并發(fā)使用 computeIfAbsent,請使用 ConcurrentHashMap。
7. 結(jié)論 ??
? computeIfAbsent 讓 Map 操作更加簡潔、高效、優(yōu)雅。
? 適用于 緩存機(jī)制、集合初始化、計(jì)數(shù) 等場景。
? 在 多線程環(huán)境下,結(jié)合 ConcurrentHashMap,避免競態(tài)條件。
? 性能通常比手動(dòng) null 檢查略優(yōu),但如果初始化邏輯復(fù)雜,手動(dòng) null 檢查可能更靈活。
到此這篇關(guān)于Java中computeIfAbsent的功能和語法小結(jié)的文章就介紹到這了,更多相關(guān)Java computeIfAbsent內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢
這篇文章主要為大家詳細(xì)介紹一下SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
實(shí)戰(zhàn)分布式醫(yī)療掛號系統(tǒng)登錄接口整合阿里云短信詳情
這篇文章主要為大家介紹了實(shí)戰(zhàn)分布式醫(yī)療掛號系統(tǒng)登錄接口整合阿里云短信詳情,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-04-04
Spring Boot 通過AOP和自定義注解實(shí)現(xiàn)權(quán)限控制的方法
這篇文章主要介紹了Spring Boot 通過AOP和自定義注解實(shí)現(xiàn)權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Java基于ProcessBuilder+FFmpeg實(shí)現(xiàn)MP4轉(zhuǎn)WAV音頻轉(zhuǎn)碼方案
在音視頻處理場景中,將MP4視頻文件中的音頻流提取為WAV格式是一個(gè)常見需求,本文介紹一種基于Java ProcessBuilder + FFmpeg的高效解決方案,通過調(diào)用命令行工具FFmpeg完成轉(zhuǎn)換,實(shí)現(xiàn)跨平臺(tái)、高質(zhì)量的音頻提取,需要的朋友可以參考下2026-03-03
cookie、session和java過濾器結(jié)合實(shí)現(xiàn)登陸程序
這篇文章主要為大家詳細(xì)介紹了cookie、session和java過濾器結(jié)合實(shí)現(xiàn)登陸程序的具體代碼,感興趣的朋友可以參考一下2016-05-05
Springboot2.0自適應(yīng)效果錯(cuò)誤響應(yīng)過程解析
這篇文章主要介紹了Springboot2.0自適應(yīng)效果錯(cuò)誤響應(yīng)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Java類加載機(jī)制實(shí)現(xiàn)步驟解析
這篇文章主要介紹了Java類加載機(jī)制實(shí)現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03

