问题
In the Java API, the implementation of HashSet is using an Object as a value for the inside HashMap,
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
but HashMap allows its value is null. I think that's not necessary to fill the value, so why is this needed?
回答1:
Because the HashSet
contract specifies that remove()
return true
if the specified object existed and was removed. To do this, it uses the wrapped HashMap#remove()
which returns the removed value.
If you were to store null
instead of an object, then the call to HashMap#remove()
would return null
, which would be indistinguishable from the result of attempting to remove a non-existent object, and the contract of HashSet.remove()
could not be fulfilled.
回答2:
but HashMap allows its value is null
Why would that matter, when the value is entirely controlled by HashSet
? That ensures that the only value ever associated with a key is PRESENT
. So if map.put
returns null
, that could only be because there was previously no entry for that key.
The value is just there because some value has to be specified, and if the value were specified as null
, that would be bad - it would make it harder to tell whether there was a value before the call to add
. If you're going to specify any non-null value, you might as well force it to be the same value all the time - you wouldn't want it to hold up garbage collection, for example.
Now if you're asking why HashSet
is implemented in terms of HashMap
rather than being a more efficient implementation which doesn't record a value at all, that's a different question, and one I don't have an answer to.
回答3:
In a Java HashMap, a mapping from an object to null is not the same as an object not being present in the map at all. Consider:
Object exists = new Object();
map.put(exists, null);
System.out.println(map.contains(exists)) // "true"
System.out.println(map.get(exists)) // "null"
Object notMapped = new Object();
System.out.println(map.contains(notMapped)) // "false"
System.out.println(map.get(notMapped)) // "null"
Also, HashMap.put() returns the old value with the key you put, which in your case is null (either because that key wasn't in the map, or its value was null).
回答4:
With a Map
, if you call put(key, null)
, you can't tell the difference between
- the key already existed, mapping to
null
- there was no mapping for that key
Since HashSet
's add
delegates to HashMap.put
, PRESENT
is required to fulfil the contract of Set.add
, which returns false
if the object already existed in the Set
:
return map.put(e, PRESENT)==null;
回答5:
I want to add one more thing:
As, HashSet add() method works like below:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Suppose, if PRESENT==null then first time when we add item in HashMap then it return null value
Object exists = new Object();
V value= map.put(exists,null); value will be null here
HashSet will return -> null == null ->> true
Second time when we add the same key in hashMap with value as Null
map.put(exists,null);
return null == null ->> true It will allows the duplicates in the hashSet.That's why JDK Developers write the PRESENT Object
回答6:
notice the ==null
part...............
来源:https://stackoverflow.com/questions/12829163/null-object-in-hashset-implementation