Map.ofEntries gives Null Pointer Exception on checking NULL key using containsKey() - java

I was fetching key from a constant map earlier using HashMap.
On passing a NULL key at containsKey(), I used to get FALSE.
To make the code look fancy, I tried java-8 over it. So, instead of HashMap, I started using Map.ofEntries to build my map
Surprisingly, I got Null Pointer Exception when a Null key was passed to containsKey() method
String str = null;
Map<String,String> hashMap = new HashMap<>();
hashMap.put("k1", "v1");
System.out.print(hashMap.containsKey(str)); // This gives false
Map<String,String> ofEntriesMap = Map.ofEntries( Map.entry("k1", "v1"));
System.out.print(ofEntriesMap.containsKey(str)); // Why this gives Null Pointer Exception ?
I am unable to figure out, why it is behaving differently at Map.ofEntries.
What is the best way to handle this situation ?

The javadoc of Map says:
Unmodifiable Maps
The Map.of, Map.ofEntries, and Map.copyOf static factory methods provide a convenient way to create unmodifiable maps. The Map instances created by these methods have the following characteristics:
They are unmodifiable. Keys and values cannot be added, removed, or updated. Calling any mutator method on the Map will always cause UnsupportedOperationException to be thrown. However, if the contained keys or values are themselves mutable, this may cause the Map to behave inconsistently or its contents to appear to change.
They disallow null keys and values. Attempts to create them with null keys or values result in NullPointerException.
...
In contrast, the javadoc of HashMap says:
Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
...

instead of HashMap, I started using Map.ofEntries to build my map
Surprisingly, I got Null Pointer Exception when a Null key was passed
to containsKey() method
The documentation for java.util.Map says, in part:
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. Attempting to query the presence of an
ineligible key or value may throw an exception, or it may simply
return false; some implementations will exhibit the former behavior
and some will exhibit the latter.
(Emphasis added.)
As #Andreas's answer already observes, the maps created via Map.ofEntries() are of such an implementation. Specifically, they disallow null keys and values. Although it is not documented whether their containsKey() methods exercise the option to throw when presented with a null argument, you need to use them with that possibility in mind.
On the other hand, as Andreas also shows, HashMap is documented to permit null keys and values, so its containsKey() method is expected to complete normally when passed a null argument.
What is the best way to handle this situation ?
You have two main choices:
If you want to continue to (directly) use a map created via Map.ofEntries() then you must avoid testing whether it contains null keys. Since you know that it cannot contain null keys, such tests are unnecessary.
If you want to rely on being able to test null keys' presence in your map, and especially if you want the option of having null keys or null values in it, then you must not use Map.ofEntries() to create it. You might, however, use Map.ofEntries() to initialize it. For example:
Map<String, String> myMap = Collections.unmodifiableMap(
new HashMap<String, String>(
Map.ofEntries(
Map.Entry("k1", "v1")
)
)
);
Note also that if you are putting fewer than 11 entries in your map, then Map.of() is a bit tidier than Map.ofEntries(). And, of course, if you don't care whether the map is modifiable then you don't have to put it into that unmodifiable wrapper.

This is implementation detail of the unmodifiable map, created by Map.ofEntries.
When you're adding null key to HashMap, it calculates hash of null equal to 0.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
But Map.ofEntries creates ImmutableCollections.Map1 in case when only one pair was provided and ImmutableCollections.MapN otherwise.
Here is implementation of ImmutableCollections.Map1::containsKey
public boolean containsKey(Object o) {
return o.equals(k0); // implicit nullcheck of o
}
You can see that comment says that NullPointerException is expected behaviour. As for ImmutableCollections.MapN::containsKey it uses explicit null-check.
public boolean containsKey(Object o) {
Objects.requireNonNull(o);
return size > 0 && probe(o) >= 0;
}
If you refer Map::containsKey Javadoc, you can see that it's explicitly said that this method may or may not produce NPE.
Returns true if this map contains a mapping for the specified key. More
formally, returns true if and only if this map contains a mapping for a key k such that Objects.equals(key, k). (There can be at most one such mapping.)
Params:
key – key whose presence in this map is to be tested
Returns:
true if this map contains a mapping for the specified key
Throws:
ClassCastException – if the key is of an inappropriate type for this map (optional)
NullPointerException – if the specified key is null and this map does not permit null keys (optional)

Related

Why does Map.of not allow null keys and values?

With Java 9, new factory methods have been introduced for the List, Set and Map interfaces. These methods allow quickly instantiating a Map object with values in one line. Now, if we consider:
Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);
The above is allowed without any exception while if we do:
Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );
It throws:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:221)
..
I am not able to get, why null is not allowed in second case.
I know HashMap can take null as a key as well as a value but why was that restricted in the case of Map.of?
The same thing happens in the case of java.util.Set.of("v1", "v2", null) and java.util.List.of("v1", "v2", null).
As others pointed out, the Map contract allows rejecting nulls...
[S]ome implementations prohibit null keys and values [...]. Attempting to insert an ineligible key or value throws an unchecked exception, typically NullPointerException or ClassCastException.
... and the collection factories (not just on maps) make use of that.
They disallow null keys and values. Attempts to create them with null keys or values result in NullPointerException.
But why?
Allowing null in collections is by now seen as a design error. This has a variety of reasons. A good one is usability, where the most prominent trouble maker is Map::get. If it returns null, it is unclear whether the key is missing or the value was null. Generally speaking, collections that are guaranteed null free are easier to use. Implementation-wise, they also require less special casing, making the code easier to maintain and more performant.
You can listen to Stuart Marks explain it in this talk but JEP 269 (the one that introduced the factory methods) summarizes it as well:
Null elements, keys, and values will be disallowed. (No recently introduced collections have supported nulls.) In addition, prohibiting nulls offers opportunities for a more compact internal representation, faster access, and fewer special cases.
Since HashMap was already out in the wild when this was slowly discovered, it was too late to change it without breaking existing code but most recent implementations of those interfaces (e.g. ConcurrentHashMap) do not allow null anymore and the new collections for the factory methods are no exception.
(I thought another reason was that explicitly using null values was seen as a likely implementation error but I got that wrong. That was about to duplicate keys, which are illegal as well.)
So disallowing null had some technical reason but it was also done to improve the robustness of the code using the created collections.
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.
In my opinion non-nullability makes sense for keys, but not for values.
The Map interface becomes a weak contract, you can't trust it's behaviour without looking at the implementation, and that is assuming you have access to see it. Java11's Map.of() does not allow null values while HashMap does, but they both implement the same Map contract - how come?
Having a null value or no value at all could not, and arguably should not be considered as a distinct scenario. When you get the value for a key and you get null, who cares if it was explicitly put there or not? there's simply no value for the supplied key, the map does not contain such key, simple as that. Null is null, there's no nullnull or null^2
Existing libraries make extensive use of map as a means to pass properties to them, many of them optional, this makes Map.of() useless as a construct for such structures.
Kotlin enforces nullability at compile time and allows for maps with null values with no known issues.
The reason behind not allowing null values is probably just due to implementation detail and convenience of the creator.
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.
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:
Introduction of Optional
Collectors.toMap(keyMapper, valueMapper) doesn't allow neither the keyMapper nor the valueMapper function to return a null value
Stream.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 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.
The documentation does not say why null is not allowed:
They disallow null keys and values. Attempts to create them with null keys or values result in NullPointerException.
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.

Java: given a HashMap, how can I use containsKey() to tell if key is explicitly mapped to a null value?

I am currently learning Java and running into some trouble with simple Java code where I have to deal with a HashMap that has missing keys and values.
Specifically, I am looking for an way to distinguish between the following two cases given a HashMap myHashMap:
myHashMap.get("myKey") is explicitly mapped to null
myHashMap does not contain an key for "myKey"
In looking around for a solution, I found the following in documentation for the get method for HashMaps in Java 8 SE, which says that:
A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map
explicitly maps the key to null. The containsKey operation may be used
to distinguish these two cases.
I am wondering if someone knows how I could use containsKey() in this way?
1) myHashMap.get("myKey") is explicitly mapped to null
Even though it mapped to null, still containsKey return true for you because there is a key called myKey exists holding a value null
2)myHashMap does not contain an key for "myKey"
That obviously return false, Since no key found with that name.
There's not much more to explain, but here's an explicit code sample.
HashMap<Integer, Object> m = new HashMap<>();
m.put(1, new Object());
m.containsKey(1); // returns true
m.containsKey(2); // returns false
You can distinguish the three possibilities (not mapped to anything vs. explicitly mapped to null vs. mapped to a non-null value) like this:
final boolean keyIsNotMappedToAnything =
!myHashMap.containsKey("myKey");
final boolean keyIsExplicitlyMappedToNull =
myHashMap.containsKey("myKey") && myHashMap.get("myKey") == null;
final boolean keyIsMappedToANonNullValue =
myHashMap.get("myKey") != null;
Yes you can use the contain key method
http://www.javaworld.com/article/2073473/java-map-get-and-map-containskey.html

Java Modifying key object inside map

I am having a problem with JAVA map. I enter an object as a key in the map. Then I modify the key and the map does not consider the object as a key of the map any more. Even though the key inside the object has been modified accordingly.
I am working with the object CoreLabel from StanfordNLP but it applies to a general case I guess.
Map <CoreLabel, String> myMap = new HashMap...
CoreLabel key = someCreatedCoreLabel
myMap.put(key, someString)
myMap.get(key) != null ----> TRUE
key.setValue("someValue");
myMap.get(key) != null ----> FALSE
I hope I was clear enough. The question is why is the last statement false? I am not a very experienced programmer but I would expect it to be true. Maybe has something to do with the CoreLabel object?
I check if .equals() still holds, and it actually does
for(CoreLabel token: myMap.keySet()) {
if(key.equals(token))
System.out.println("OK");
}
This is explicitly documented in the Map Javadoc as dangerous and unlikely to work:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.
The problem is that in modifying the value of the key, now the hash code of the key has changed as well. A HashMap will first use the hash code of the key to determine if it exists. The modified hash code didn't exist in the map, so it didn't even get to try the step of using the equals method. That's why it's a bad idea to change your key objects while they're in a HashMap.

Why Hashtable does not allow null keys or values?

As specified in JDK documentation, Hashtable does not allow null keys or values. HashMap allows one null key and any number of null values. Why is this?
Hashtable is the older class, and its use is generally discouraged. Perhaps they saw the need for a null key, and more importantly - null values, and added it in the HashMap implementation.
HashMap is newer, and has more advanced capabilities, which are basically just an improvement on the Hashtable functionality. When HashMap was created, it was specifically designed to handle null values as keys and handles them as a special case.
Edit
From Hashtable JavaDoc:
To successfully store and retrieve objects from a Hashtable, the
objects used as keys must implement the hashCode method and the equals method.
Since null isn't an object, you can't call .equals() or .hashCode() on it, so the Hashtable can't compute a hash to use it as a key.
The main reason why Hashtable and ConcurrentHashMap do not allow null keys or values is because of the expectation that they are going to be used in a multi-threaded environment. For a minute, let's assume that null values are allowed. In this case the hashtable's "get" method has ambiguous behavior. It can return null if the key is not found in the map or it can return null if the key is found and its value is null. When a code expects null values, it usually checks if the key is present in the map so that it can know whether the key is not present or the key is present but value is null. Now this code breaks in a multi-threaded environment. Let's take a look at below code:
if (map.contains(key)) {
return map.get(key);
} else {
throw new KeyNotFoundException;
}
In the above code, let's say thread t1 calls the contains method and finds the key and it assumes that key is present and is ready for returning the value whether it is null or not. Now before it calls map.get, another thread t2 removes that key from the map. Now t1 resumes and returns null. However as per the code, the correct answer for t1 is KeyNotFoundException because the key has been removed. But still it returns the null and thus the expected behavior is broken.
Now, for a regular HashMap, it is assumed, that it is going to get called by a single thread, hence there is no possibility of key getting removed in the middle of "contains" check and "get". So HashMap can tolerate null values. However for Hashtable and ConcurrentHashMap, the expectations are clear that multiple threads are going to act on the data. Hence they cannot afford to allow null values and give out incorrect answer. Same logic goes for keys. Now the counter argument can be - the contains and get steps could fail for non null values for Hashtables and ConcurrentHashMaps, because another thread can modify the map/table before the second step gets executed. That is correct, it can happen. But since Hashtables and ConcurrentHashMaps do not allow null keys and values, it is not necessary for them to implement contains and get check in the first place. They can directly get the value because they know that if the get method returns null, the only reason for that is the key is not present and not because the value could be null. The contains and get check is necessary only for HashMaps because they allow the null values and thus need to resolve the ambiguity about whether the key is not found or the value is null.
The reason is the reason on the accepted answer: Hashtable is old.
However, the use of Hashtable IS NOT discouraged in favor of HashMap in every scenario.
Hashtable is synchronized, so it is THREAD-SAFE. HashMap is not.
Neither Hashtable nor ConcurrentHashMap support null keys or values. HashMap does.
If you want a drop-in replacement that doesn't require anything else than changing the class and works in every scenario, there is none. The most similar option would be ConcurrentHashMap (which is thread safe but doesn't support locking the whole table):
This class is fully interoperable with Hashtable in programs that rely
on its thread safety but not on its synchronization details.
HashMap is a better replacement for single threaded applications or any time synchronization is not a requirement, because of the performance impact synchronization introduces.
Sources:
Hashtable
HashMap
ConcurrentHashMap
so to conclude
Because in HashTable when you put an element it will take into account key and value hash. Basically you will have something like :
public Object put(Object key, Object value){
key.hashCode();
//some code
value.hashCode();
}
HashTable - Does not allow null keys
This is because, in put(K key, V value) method, we have key.hashcode() which throws null pointer exception.
HashTable - Does not allow null value
This is because, in put(K key, V value) method we have if(value==null){throw new NullPointerException
HashMap allows null values as it doesn't have any checks like HashTable, while it allows only one null key. This is done with the help of putForNullKey method, which add the value to the 0th index of the internal Array every time the key is provided as null
Default Hashtable implementation has null check which thorws null pointer exception.
Later on java developers might have realized the importance of null keys(for some default value etc) and values and that why HashMap got introduced.
For HashMap, null check is there for keys if the key is null then that element will be stored in a location where hashcode is not required.
Hashtable is a class which came with the first version of java. When it was released Java engineers tried to discourage the use of null keys or maybe did not realize its usefulness. So, they did not allow it in the Hashtable.
The put method to insert key value pair in Hashtable throws NullPointerException if value is null. Since Hashtable is based on hashing mechanism, hash is calculated for keys, which throws NullPointerException if key is null.
Later Java engineers must have realized that having a null key and values has its uses like using them for default cases. So, they provided HashMap class with collection framework in Java 5 with capability of storing null key and values.
The put method to insert key value pair in HashMap checks for null key and stores it at the first location of the internal table array. It isn’t afraid of the null values and does not throw NullPointerException like Hashtable does.
Now, there can be only one null key as keys have to be unique although we can have multiple null values associated with different keys.
Hash table is very old class , from JDK 1.0
To understand this, first of all we need to understand comments written on this class by author.
“This class implements a hashtable, which maps keys to values. Any non-null object can be used as a key or as a value.
To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the
hashCode method and the equals method.”
HashTable class is implemented on hashing mechanism, that’s mean to store any key-value pair, its required hash code of key object. If key would be null, it will not able to given hash ,it will through null pointer exception and similar case for value
it is throwing null if the value is null.
But later on it was realized that null key and value has its own importance that is
why one null key and multiple null values are allowed in later implemented classes like HashMap class.
For hash map null keys will allow and there is a null check is there for keys
if the key is null then that element will be stored in a zero location in Entry array. null key we can use for some default value..
=> Hashtable methods are synchronised it neveruses object based locking.
HashMap implements by considering it special
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Java 8 you cannot infer types for hash table.
private Map<String,String> hashtable = new Hashtable<>(); // Not Allowed
private Map<String,String> hashtable = new HashMap<>(); // Allowed
Just as #Jainendra said, HashTable doesn't allow null key for call key.hashCode() in put().
But it seems that no one clearly answer why null value isn't allowed.
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
#SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
The null check in put doesn't explain why null value is illegal, it just ensure non-null invariant.
The concrete answer for not allow null value is HashTable will call value.equals when call contains/remove.
Hashtable does not allow null keys but HashMap allows one null key and any number of null values. There is a simple explanation behind that.
put() method in hashmap does not call hashcode() when null is passed as key and null Key is handled as a special case. HashMap puts null key in bucket 0 and maps null as key to passed value.
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
As seen in the algorithm the put() method checks if the key is null and call putForNullKey(value) and return. This putForNullKey will create a entry in bucket at 0 index. Index zero is always reserved for null key in the bucket.
On the other hand, in case of hashtable objects used as keys must implement the hashCode method and the equals method. Since null is not an object, it can’t implement these methods.
HashTable - Does not allow null keys
This is because, in put(K key, V value) method, we have key.hashcode() which throws null pointer exception.
HashTable - Does not allow null value
This is because, in put(K key, V value) method we have if(value==null){throw new NullPointerException
HashMap allows null values as it doesn't have any checks like HashTable, while it allows only one null key. This is done with the help of putForNullKey method, which add the value to the 0th index of the internal Array every time the key is provided as null

Why is it useful to have null values or null keys in hash maps?

Hashtable does not allow null keys or values, while HashMap allows null values and 1 null key.
Questions:
Why is this so?
How is it useful to have such a key and values in HashMap?
1. Why is this so?
HashMap is newer than Hashtable and fixes some of its limitations.
I can only guess what the designers were thinking, but here are my guesses:
Hashtable calculates a hash for each key by calling hashCode on each key. This would fail if the key were null, so this could be a reason for disallowing nulls as keys.
The method Hashtable.get returns null if the key is not present. If null were a valid value it would be ambiguous as to whether null meant that the key was present but had value null, or if the key was absent. Ambiguity is bad, so this could be a reason for disallowing nulls as values.
However it turns out that sometimes you do actually want to store nulls so the restrictions were removed in HashMap. The following warning was also included in the documentation for HashMap.get:
A return value of null does not necessarily indicate that the map contains no mapping for the key; it is also possible that the map explicitly maps the key to null.
2. How is it useful to have such a key and values in HashMap?
It is useful to explicitly store null to distinguish between a key that you know exists but doesn't have an associated value and a key that doesn't exist. An example is a list of registered users and their birthdays. If you ask for a specific user's birthday you want to be able to distinguish between that user not existing and the user existing but they haven't entered their birthday.
I can't think of any (good) reason for wanting to store null as a key, and in general I'd advise against using null as a key, but presumably there is at least one person somewhere that needs that keys that can be null.
Well, I think Mark Byers answered perfectly, so just a simple example where null values and keys can be useful:
Imagine you have an expensive function that always returns the same result for the same input. A map is a simple way for caching its results. Maybe sometimes the function will return null, but you need to save it anyway, because the execution is expensive. So, null values must be stored. The same applies to null key if it's an accepted input for the function.
HashTable is very old class , from JDK 1.0.The classes which are in place from JDK 1.0 are called Legacy classes and by default they are synchronized.
To understand this, first of all you need to understand comments written on this class by author.
“This class implements a hashtable, which maps keys to values. Any non-null object can be used as a key or as a value. To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.”
HashTable class is implemented on hashing mechanism, means to store any key-value pair, its required hash code of key object. HashTable calculates a hash for each key by calling hashCode on each key. This would fail If key would be null, it will not be able to give hash for null key, it will throw NullPointerException and similar is the case for value, throwing null if the value is null.
But later on it was realised that null key and value has its own importance, then revised implementations of HashTable were introduced like HashMap which allow one null key and multiple null values.
For HashMap, it allows one null key and there is a null check for keys, if the key is null then that element will be stored in a zero location in Entry array.
We cannot have more than one Null key in HashMap because Keys are unique therefor only one Null key and many Null values are allowed.
USE - Null key we can use for some default value.
Modified and better implementation of HashTable was later introduced as ConcurrentHashMap.
In addition to what answered by Mark Bayers,,
Null is considered as data and it has to be stored as a value for further checking. In many cases null as value can be used to check if key entry is there but no value is assigned to it, so some action can be taken accordingly. This can be done by first checking if key is there, and then getting value.
There is one more case in which just put whatever data is coming(without any check). All the checks are applied to it after getting it.
Whereas null as a key, i think can be used to define some default data. Usually null as a key does not make much sense.
Sir HashMap is also internally uses hashCode() method for inserting an element in HashMap, so I think this will be not the proper reason for "why HashTable allow null key"
It will make the Map interface easier to use / less verbose. null is a legitimate value for reference types. Making the map able to handle null keys and values will eliminate the need for null checking before calling the api. So the map api create less "surprises" during runtime.
For example, it is common that map will be used to categorize a collection of homogeneous objects based a single field. When map is compatible with null, the code will be more concise as it is just a simple loop without any if statement (of course you will need to make sure the collection does not have null elements). Fewer lines of code with no branch / exception handling will be more likely to be logically correct.
On the other hand, not allowing null will not make the map interface better/safer/easier to use. It is not practical to rely on the map to reject nulls - that means exception will be thrown and you have to catch and handle it. Or, to get rid of the exception you will have to ensure nothing is null before calling the map methods - in which case you don't care if map accepts null since you filtered the input anyway.

Categories