With Java 9, new factory methods have been introduced for the List
, Set
and Map
interfaces. These methods allow quickly instantiating a Ma
While HashMap does allow null values, Map.of
does not use a HashMap
and throws an exception if one is used either as key or value, as documented:
The Map.of() and Map.ofEntries() static factory methods provide a convenient way to create immutable maps. The Map instances created by these methods have the following characteristics:
...
They disallow null keys and values. Attempts to create them with null keys or values result in NullPointerException.
The major difference is: when you build your own Map the "option 1" way ... then you are implicitly saying: "I want to have full freedom in what I am doing".
So, when you decide that your map should have a null key or value (maybe for the reasons listed here) then you are free to do so.
But "option 2" is about a convenience thing - probably intended to be used for constants. And the people behind Java simply decided: "when you use these convenience methods, then the resulting map shall be null-free".
Allowing for null values means that
if (map.contains(key))
is not the same as
if (map.get(key) != null)
which might be a problem sometimes. Or more precisely: it is something to remember when dealing with that very map object.
And just an anecdotal hint why this seems to be a reasonable approach: our team implemented similar convenience methods ourselves. And guess what: without knowing anything about plans about future Java standard methods - our methods do the exact same thing: they return an immutable copy of the incoming data; and they throw up when you provide null elements. We are even so strict that when you pass in empty lists/maps/... we complain as well.
Allowing nulls in maps has been an error. We can see it now, but I guess it wasn't clear when HashMap
was introduced. NullpointerException
is the most common bug seen in production code.
I would say that the JDK goes in the direction of helping developers fight the NPE plague. Some examples:
Optional
Collectors.toMap(keyMapper, valueMapper)
doesn't allow neither the keyMapper
nor the valueMapper
function to return a null
valueStream.findFirst()
and Stream.findAny()
throw NPE if the value found is null
So, I think that disallowing null
in the new JDK9 immutable collections (and maps) goes in the same direction.
The documentation does not say why null
is not allowed:
They disallow
null
keys and values. Attempts to create them withnull
keys or values result inNullPointerException
.
In my opinion, the Map.of()
and Map.ofEntries()
static factory methods, which are going to produce a constant, are mostly formed by a developer at the compile type. Then, what is the reason to keep a null
as the key or the value?
Whereas, the Map#put
is usually used by filling a map at runtime where null
keys/values could occur.
Not all Maps allow null as key
The reason mentioned in the docs of Map.
Some map implementations have restrictions on the keys and values they may contain. For example, some implementations prohibit null keys and values, and some have restrictions on the types of their keys. Attempting to insert an ineligible key or value throws an unchecked exception, typically NullPointerException or ClassCastException.
Exactly - a HashMap
is allowed to store null, not the Map
returned by the static factory methods. Not all maps are the same.
Generally as far as I know it has a mistake in the first place to allow nulls in the HashMap
as keys, newer collections ban that possibility to start with.
Think of the case when you have an Entry in your HashMap
that has a certain key and value == null. You do get, it returns null
. What does the mean? It has a mapping of null
or it is not present?
Same goes for a Key
- hashcode from such a null key has to treated specially all the time. Banning nulls to start with - make this easier.