In the case of HashMap,it replaces the old value with the new one in case of key duplication.In the case of HashSet, the item isn't inserted at all. Since HashSet is implementing HashMap internally. Why are these two insertions handled differently?
1 Answer
HashMap is replaces old value with a new one, when you are inserting it with the same key. Example:
Map<Integer, String> map = new HashMap<>(); map.put(1, "first put"); System.out.println(map.get(1)); // <-- prints `first put` map.put(1, "second put"); System.out.println(map.get(1)); // <-- prints `second put` So basically when the new value arrives with a key already present in the map, it just replaces the value for this key, because even if the key is the same, new value can be different from the one associated with this key in the map.
In case of the HashSet, there is only key, there is no value. So when already existing key arrives, there is noting to do, it is already there.
Regarding internal implementation of the HashSet, indeed it uses HashMap internally, but what it does, it just create one object:
// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); and when you add some key to set, it just calls put(key, present) on this map, using the same object for all entries(in order not to allocate lots of useless objects:
public boolean add(E e) { return map.put(e, PRESENT)==null; } So all in all, indeed HashSetuses internally HashMap, and when you put something to hash map, new value is always assigned instead of the old one, however the key isn't, and as HashSet uses its keys as the keys in HashMap, then if you put duplicate key in HashSet it wouldn't remove the old one. Example:
Lets assume we have some entity class which has 2 fields, int id, and string name, but only id participates in equals method implementation.
Entity entity1 = new Entity(1, "some name"); Entity entity2 = new Entity(1, "some other name"); System.out.println(entity1.equals(entity2)); // returns true Map<Integer, Entity> map = new HashMap<>(); map.put(entity1.id, entity1); map.put(entity2.id, entity2); System.out.println(map.get(entity1.id).name); // returns "some other name" Map<Entity, String> keyMap = new HashMap<>(); keyMap.put(entity1, entity1.name); keyMap.put(entity2, entity2.name); System.out.println(keyMap.keySet()); // returns [Entity{id=1, name='some name'}] System.out.println(keyMap.values()); // returns [some other name] Set<Entity> set = new HashSet<>(); set.add(entity1); set.add(entity2); System.out.println(new ArrayList<>(set)); // returns [Entity{id=1, name='some name'}] Also you shouldn't rely on that too, as its internal implementation details which can be changed in future releases, HashMap and HashSet use equals methods the check if the object is the same. If you have an two objects which equals returns true but you still want to treat the differently, most probably there is some problem in you Entity design, however there are rare cases when inded thats the desired effect, in such cases you can take a look at the IdentityHashMap.
5 Comments
PRESENT object is static, meaning that all instances of HashSet would put this particular instance of Object as a value to underlying map, when you will call add on the HashSet, from the current implementation of HashMap I see that indeed it will reassign new value even if they are equal, but you shouldnt rely on it, as its the internal implementation details. However the key wouldn't be reassigned if its already present. If it matters to you which object was assigned, even if they are equal, then you should probably take a look at IdentityHashMapHashSet key wouldn't be overwritten, in HashMap key wouldn't be overwritten too, however value would be. You can check last example in my answer, it shows this behaviour for all case
HashMaps work likeMaps, andHashSets work likeSets.Maps andSets are two entirely different concepts, and you shouldn't let the fact that aHashSetmight be implemented on top of aHashMaplead you to believe otherwise.HashSetis actually implemented as aHashMap, where the value is ignored, i.e. any value added to theSetis put into the map as a key with a dummy value:public boolean add(E e) { return map.put(e, PRESENT)==null; }