Serguey Asael Shinder: getOrDefault returns null when the key is there and maps to null
Map.getOrDefault reads like "the value, or my fallback if there isn't one". The javadoc is narrower than that, and the gap is a NullPointerException waiting for the first map that contains a null value.
Map<String, String> settings = new HashMap<>();
settings.put("region", null); // e.g. copied from a config file with an empty entry
String region = settings.getOrDefault("region", "eu-west");
region.length(); // NullPointerException: region is null
Why. The Map.getOrDefault javadoc) says it returns "the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key". A key stored with null has a mapping. Its value is null, so null is what comes back. The default only applies when the key is absent.
The surprise is the inconsistency. Other default methods on the same interface treat a null value as if the key were missing:
merge): "If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value."computeIfAbsent) also computes when the current value is null, andputIfAbsentreplaces a null value.

So in one map, merge sees the key as empty while getOrDefault sees it as present. Code that mixes them — fill with merge, read with getOrDefault — works until a null value arrives from somewhere else.
Where it bites. Maps built from external data: parsed JSON or YAML with explicit nulls, rows from a database, HashMap.putAll from a map that allowed nulls. Map.of and Map.copyOf reject null values outright, so the problem lives in mutable maps and in anything deserialised into one.
The fixes, by intent.
- Null should mean "use the default": say so explicitly.
String region = Objects.requireNonNullElse(settings.get("region"), "eu-west");
// or
String region = Optional.ofNullable(settings.get("region")).orElse("eu-west");
- Null values should not exist in this map: stop them at the door — reject on load, or build the map with
Map.copyOf, which throws on null values instead of storing them. - You must distinguish "absent" from "present but null": use
containsKeyand handle the two cases separately;getOrDefaultcannot express that difference.
No JDK runs on the machine this note was written on; the behaviour described is the one specified in the linked javadoc.