Map.computeIfAbsent calls its mapping function when the key is absent or mapped to null, and stores only a non-null result.
Java computeIfAbsent: null means no new mapping
The null case has two meanings
A missing receipt key returns null from the loader, so the map remains without that key. Another key already mapped to null is treated as needing a value; the loader fills it. containsKey reveals the initial null mapping, while get cannot distinguish it from absence.
If a loader can fail, prefer an explicit result or exception over returning null ambiguously. The mapping function should not structurally edit the same map. merge solves a different case: combining a supplied value with an existing one.
Do not infer once-only concurrent loading
This program uses a plain HashMap and no concurrent calls. Loading from a remote service needs a separate cache-stampede, timeout and stale-value policy. A ConcurrentHashMap's computeIfAbsent contract differs from the default Map operation and should be tested under its own boundary.
Working program
import java.util.HashMap;
import java.util.Map;
public class ReceiptLookupCache {
public static void main(String[] args) {
Map<String, String> receiptState = new HashMap<>();
receiptState.put("R-41", null);
System.out.println("present-null=" + receiptState.containsKey("R-41"));
System.out.println("missing=" + receiptState.computeIfAbsent("R-99", id -> null));
System.out.println("stored-missing=" + receiptState.containsKey("R-99"));
System.out.println("loaded=" + receiptState.computeIfAbsent("R-41", id -> "reviewed"));
}
}Output
present-null=true
missing=null
stored-missing=false
loaded=reviewedCosts and boundaries
Expected HashMap lookup cost depends on hash distribution. A mapping function may do arbitrary work, so do not report the whole operation as constant time without including that work. This fixture does not test concurrent loading.
Common Mistakes
- Do not use get == null when presence matters.
- Do not assume a null loader result is cached.
- Do not infer a cross-thread single-loader guarantee from a plain Map example.
Read next
Java HashMap: keys, collisions, and update operations, Java Map.merge: combine a value or remove its mapping, Java ConcurrentHashMap: atomic updates and weakly consistent reads, Spring cache keys: separate tenants and test the loader count.
