I believe it is, at least in part, to allow you to combine containsKey
and get
into a single call. If the map can hold nulls, there is no way to tell if get
is returning a null because there was no key for that value, or just because the value was null.
Why is that a problem? Because there is no safe way to do that yourself. Take the following code:
if (m.containsKey(k)) {
return m.get(k);
} else {
throw new KeyNotPresentException();
}
Since m
is a concurrent map, key k may be deleted between the containsKey
and get
calls, causing this snippet to return a null that was never in the table, rather than the desired KeyNotPresentException
.
Normally you would solve that by synchronizing, but with a concurrent map that of course won't work. Hence the signature for get
had to change, and the only way to do that in a backwards-compatible way was to prevent the user inserting null values in the first place, and continue using that as a placeholder for "key not found".