I have some questions about Java Collection objects...
When we add objects to a collection like HashSet or HashMap, how the the objects internally stored?
Why doesn't Hashtable allow null values?
You're not adding an object to the collection. You're adding a reference.
As for why HashTable doesn't allow null keys and values - it's just a design decision; in some cases it's helpful to disallow null keys, while in others it's annoying. HashMap does allow for both null keys and values, for example. There are various reasons for prohibiting nulls:
Usually a null key or a null value indicates a programming error in the calling code. Rejecting this at the point of insertion makes it far easier to find the error than waiting until the code fetches a value and then has an unexpected null.
If you know that values in a map can't be null, then you don't need to do a separate check for containment and then fetch: you can fetch, and if the result is null, you know the key was missing.
It takes a bit more work to handle null keys in the map implementation. While null values can sometimes be useful, null keys almost never are.
Hashtable does not allow null values because it uses the equals and hashcode methods of the objects added to it
http://en.wikipedia.org/wiki/Hash_function
Related
This question already has answers here:
Why Hashtable does not allow null keys or values?
(10 answers)
Closed 4 years ago.
I know about null key is not allowed in Hashtable because to store element in Hashtable hash code must required. But if key is null it will unable to calculate hash code for null key. But I don't understand but what is the exact reason in mind for Sun developers not to allow null value.
Someone says there is null check for value inside put method implementation and that's why it throws NullPointerException. But my question is why that null value check. Is there any specific reason behind it.
I went through lots of read but no got satisfied answer. Some one says there is ambiguity if there is null value and if you try to retrieve value using get() method it will return null and this null is because of actual value is null or key is missing that's why null and could not predict reason. So i need pin point answer with proof.
You will get NULL for value if you do
hashtable.get("key")
and "key" is not in the map, then you don't need to store null values.
If you would be able to store null, you will never know what you had: null mapping or that is a missing mapping.
Hashtable is considered legacy code. You should use HashMap and it allow null for values and also one key can be null.
EDIT
After deeper search I may have argument for such decision. Hashtable is synchronized (and HashMap isn't).
From JavaDoc:
Unlike the new collection implementations, Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.
As you can see successor of Hashtable is not HashMap as I previously write but ConcurrentHashMap. I was surprised that ConcurrentHashMap does not allows null. I start digging and found this:
From the author of ConcurrentHashMap himself (Doug Lea):
The main reason that nulls aren't allowed in ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that may be just barely tolerable in non-concurrent maps can't be accommodated. The main one is that if map.get(key) returns null, you can't detect whether the key explicitly maps to null vs the key isn't mapped. In a non-concurrent map, you can check this via map.contains(key), but in a concurrent one, the map might have changed between calls.
So maybe authors of Hashtable have the same reason as authors of ConcurrentHashMap
Having a null value is still considered a bad decision in HashMap and the new Map classes and the static factory methods in java-9 prove that:
Map.of("test", null)
will throw a NulPointerException
From Java Documentation
To successfully store and retrieve objects from a hash table, the
objects used as keys must implement the hashCode method and the equals
method
Null is not an object, so can not call .equals() or .hashCode() on it, so the Hashtable can't compute a hash to use it as a key
Hashtable containsValue(Object value) function throw NullPointerException if the value is null so for the value also not allowed null
The JavaDoc of ConcurrentHashMap says this:
Like Hashtable but unlike HashMap, this class does not allow null to be used as a key or value.
My question: Why?
2nd question: Why doesn't Hashtable allow null?
I've used a lot of HashMaps for storing data. But when changing to ConcurrentHashMap I got several times into trouble because of NullPointerExceptions.
From the author of ConcurrentHashMap himself (Doug Lea):
The main reason that nulls aren't allowed in ConcurrentMaps
(ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that
may be just barely tolerable in non-concurrent maps can't be
accommodated. The main one is that if map.get(key) returns null, you
can't detect whether the key explicitly maps to null vs the key isn't
mapped. In a non-concurrent map, you can check this via
map.contains(key), but in a concurrent one, the map might have changed
between calls.
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".
Josh Bloch designed HashMap; Doug Lea designed ConcurrentHashMap. I hope that isn't libelous. Actually I think the problem is that nulls often require wrapping so that the real null can stand for uninitialized. If client code requires nulls then it can pay the (admittedly small) cost of wrapping nulls itself.
You can't synchronize on a null.
Edit: This isn't exactly why in this case. I initially thought there was something fancy going on with locking things against concurrent updates or otherwise using the Object monitor to detect if something was modified, but upon examining the source code it appears I was wrong - they lock using a "segment" based on a bitmask of the hash.
In that case, I suspect they did it to copy Hashtable, and I suspect Hashtable did it because in the relational database world, null != null, so using a null as a key has no meaning.
I guess that the following snippet of the API documentation gives a good hint:
"This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details."
They probably just wanted to make ConcurrentHashMap fully compatible/interchangeable to Hashtable. And as Hashtable does not allow null keys and values..
ConcurrentHashMap is thread-safe. I believe that not allowing null keys and values was a part of making sure that it is thread-safe.
I don't think disallowing null value is a correct option.
In many cases, we do want do put a key with null value into the con-current map. However, by using ConcurrentHashMap, we cannot do that.
I suggest that the coming version of JDK can support that.
Today i read in a book:-
"HashMap allows one null key and multiple null values in a collection."
HashMap<Object,Object> ih=new HashMap<Object,Object>();
Object o1=null;
Integer o2=null;
ih.put(o1,null);
ih.put(new Integer(2),null);
ih.put(o2,new Integer(3));
This example is putting two null object references in the map.
But the following Example is successfully compiling and running...
I cant figure out why?
"HashMap allows one null key and multiple null values in a
collection."
Allow one null key means if you keep adding null key, it will overwrite the previous values. Actually, this is true for any key in HashMap.
The bottom line is - HashMap allows only one key
e.g. if you print the size of your Hashmap it will be 2.
System.out.println("size:: "+ih.size());
What the book probably means is this:
among the possible keys of a HashMap<X, Y>, there can be a null key;
whatever the key (even null!), the value associated to this key can be null.
Therefore, it is perfectly normal that your code works. It is just that the book's text here is quite confusing.
But of course, this entirely depends on the Map implementation; some of them, such as ConcurrentHashMap, will not allow null keys or values.
Is there any way to make a key for searching the values in the collections and not returning null keys and values?
Just implement your own Map which throws for example IllegalArgumentException instead of returning null.
All with all, this makes no sense. If you want, just test the existence of the key using Map#containsKey() and/or check if the returned value is null or not.
The JavaDoc of ConcurrentHashMap says this:
Like Hashtable but unlike HashMap, this class does not allow null to be used as a key or value.
My question: Why?
2nd question: Why doesn't Hashtable allow null?
I've used a lot of HashMaps for storing data. But when changing to ConcurrentHashMap I got several times into trouble because of NullPointerExceptions.
From the author of ConcurrentHashMap himself (Doug Lea):
The main reason that nulls aren't allowed in ConcurrentMaps
(ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that
may be just barely tolerable in non-concurrent maps can't be
accommodated. The main one is that if map.get(key) returns null, you
can't detect whether the key explicitly maps to null vs the key isn't
mapped. In a non-concurrent map, you can check this via
map.contains(key), but in a concurrent one, the map might have changed
between calls.
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".
Josh Bloch designed HashMap; Doug Lea designed ConcurrentHashMap. I hope that isn't libelous. Actually I think the problem is that nulls often require wrapping so that the real null can stand for uninitialized. If client code requires nulls then it can pay the (admittedly small) cost of wrapping nulls itself.
You can't synchronize on a null.
Edit: This isn't exactly why in this case. I initially thought there was something fancy going on with locking things against concurrent updates or otherwise using the Object monitor to detect if something was modified, but upon examining the source code it appears I was wrong - they lock using a "segment" based on a bitmask of the hash.
In that case, I suspect they did it to copy Hashtable, and I suspect Hashtable did it because in the relational database world, null != null, so using a null as a key has no meaning.
I guess that the following snippet of the API documentation gives a good hint:
"This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details."
They probably just wanted to make ConcurrentHashMap fully compatible/interchangeable to Hashtable. And as Hashtable does not allow null keys and values..
ConcurrentHashMap is thread-safe. I believe that not allowing null keys and values was a part of making sure that it is thread-safe.
I don't think disallowing null value is a correct option.
In many cases, we do want do put a key with null value into the con-current map. However, by using ConcurrentHashMap, we cannot do that.
I suggest that the coming version of JDK can support that.