I have a class which I want to set up as keys in HashMap. I already have implemented the compareTo method for that class. But still when I do:
map.put(new MyKey(dummyArguements) , dummyValue );
System.out.println(map.get( new MyKey(dummyArguements) ) );
I get null.
So that means hashmap is not able to identify that the two keys (for get & put call) are same.
Could someone help me here please ?
You need to implement hashCode() and equals(). compareTo() is additionally required for sorted map/set.
See this question for details.
You should implement equals() and hashCode(). Your class should also be immutable. If it is mutable, it's hash code can change after adding it to map. Then the map can have problems finding it.
1) In general for collections, what you want to override is the equals() method (and also the hashcode() method) for your class. compareTo()/Comparable and Comparator are typically used for sorting and only take the place of using the equals() method for object equivalance in some cases - examples are implementers of SortedSet such as TreeSet.
2) Please conform to Java naming standards in your code. Your class names should be capitalized... e.g new MyKey(dummyArguments). See http://www.oracle.com/technetwork/java/codeconventions-135099.html#367 (and http://www.oracle.com/technetwork/java/codeconvtoc-136057.html) for more detail.
HashMap doesn't check compareTo();
HashMap checks hashCode() and equals().
When using Collections that rely on hashing like Map and Set you have to implement the equals() and hashCode() to guarantee correct functionality. If you don't a new myKey will always be different from the key stored in the map because it uses the default implementations of equals() and hashCode().
Do you have the hashCode() defined? compareTo is needed for sorting.
As of java8 you should also implement Comparable (adding compareTo) because if the number of hash clashes exceeds 11, HashMap stores the entries in a binary tree. If you don't, performance suffers
Related
I was just wondering if there is any consideration to have in account when saving our own objects in a TreeMap. Something similar when we save our own objects as keys in a hashmap that we need to override equals and hashcode method to be able to retrieve them later. In a treemap there is no hash, a black red algorith is used, but I don't know if there is something special to do.
If so, could you tell me if there is something to have in account?
Thanks
The javadoc says:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time
So you need to implement a natural ordering correctly, or implement a Comparator correctly.
It also says:
Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
So, if you want to obey the general contract of Map (and you should, generally), the compareTo() method must be consistent with equals(), which means that you need to correctly implement an equals() method, and transitively, a hashCode() method, and that you must make sure that a.equals(b) iff e.compareTo(b) == 0.
Most of the times, people screw up because they implement a compareTo/compare method that returns 0 for two objects, and still expect these two objects to be considered different by the map.
Apart from equals and compareTo transitivity there's one more thing that's terribly important.
Your keys, or at least the fields you're using for comparison, should be immutable.
And you can actually use anything as a key for a TreeMap as long as you provide custom Comparator in its constructor.
I'm trying to understand java.util.Collection and java.util.Map a little deeper but I have some doubts about HashSet funcionality:
In the documentation, it says: This class implements the Set interface, backed by a hash table (actually a HashMap instance). Ok, so I can see that a HashSet always has a Hashtable working in background. A hashtable is a struct that asks for a key and a value everytime you want to add a new element to it. Then, the value and the key are stored in a bucket based on the key hashCode. If the hashcodes of two keys are the same, they add both key values to the same bucket, using a linkedlist. Please, correct me if I said something wrong.
So, my question is: If a HashSet always has a Hashtable acting in background, then everytime we add a new element to the HashSet using HashSet.add() method, the HashSet should add it to its internal Hashtable. But, the Hashtable asks for a value and a key, so what key does it use? Does it just uses the value we're trying to add also as a key and then take its hashCode? Please, correct me if I said something wrong about HashSet implementation.
Another question that I have is: In general, what classes can use the hashCode() method of an java object? I'm asking this because, in the documentation, it says that everytime we override equals() method we need to override hashCode() method. Ok, it really makes sense, but my doubt is if it's just a recommendation we should do to keep everything 'nice and perfect' (putting in this way), or if it's really necessary, because maybe a lot of Java defaults classes will constantly uses hashCode() method of your objects. In my vision, I can't see other classes using this method instead of those classes related to Collections. Thank you very much guys
If you look at the actual javacode of HashSet you can see what it does:
// 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;
}
So the element you are adding is the Key in the backing hashmap with a dummy value as the value. this dummy value is never actually used by the hashSet.
Your second question regarding overriding equals and hashcode:
It is really necessary to always override both if you want to override either one. This is because the contract for hashCode says equal objects must have the same hashcode. the default implementation of hashcode will give different values for each instance.
Therefore, if you override equals() but not hashcode() This could happen
object1.equals(object2) //true
MySet.add(object1);
MySet.contains(object2); //false but should be true if we overrode hashcode()
Since contains will use hashcode to find the bucket to search in we might get a different bucket back and not find the equal object.
If you look at the source for HashSet (the source comes with the JDK and is very informative), you will see that it creates an object to use as the value:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
Each value that is added to the HashSet is used as a key to the backing HashMap with this PRESENT object as the value.
Regarding overriding equals() whenever you override hashCode() (and vice versa), it is very important that these two methods return consistent results. That is, they should agree with one another. For more details, see the book Effective Java by Josh Bloch.
Is there some implementation of java.util.Map that does not uses HashCode?
I have the following problem:
I store an object associated to another object on a HashMap;
Change a property from the key object used on step 1;
As the hashcode is used to store the keys on the regular implementation of HashMap, when I perform a get() on the HashMap, I get null, because the old object hashCode was different at step 1.
Is there a solution for that? Or should I really use just immutable fields for my equals / hashCode methods?
IdentityHashMap uses the Object identity instead of the hashCode; however that does mean that you require the original object used as key to retrieve the value of the map. Other options would be redefine the hashcode to exclude the mutable parts of the object, or - if you can't redefine the hashCode for some reason - wrap the object in another object which provides a stable hashCode.
You would be well advised to use an immutable key, and to re-insert the key/value pair into Map, rather than mutating the key in-place. As you discovered, that just leads to weird bugs.
If this isn't an option for you, then see if you can ignore the mutable property in the hashCode() method, so that the hash code doesn't change. If that's the only property of the class, though, that's not a good idea.
You may be able to get away with using TreeMap, which I don't think uses hashCode(). However, it does require consistency between the key's compareTo() and equals() methods, so you may just end up with the same problem as before if the return values of those methods can change.
All Maps should use immutable objects for keys. True for Python; true for Java.
If you implement equals and hashCode using only immutable fields you should be fine.
How about removing and adding it again ?
On Step 2, You can remove the element added in Step 1 and again add it with new latest properties set. This way when you are try to get in Step 3, you will find it.
Try it.
I think modify the key object in map is not a good practice.
But if you really want, you can override the hashCode() and remember to override the equal() method.
All associative containers use comparing or hash code, so I would like to recommend you using immutable fields for equals() / hashCode() methods.
Override equals and hashCode methods if you don't want original implementation.
I have a hashmap of the type HashMap<Long, ArrayList<String>>.
I need to override the equals() and hashCode() methods to gain in performance. What should be the overriding function like, for this case ?
I knew the exact no. of elements I would be putting in the HashMap, thus I have used initial Capacity = (No. Of Elements that will be put)/ 0.75
where 0.75=> load factor,
Is this correct?
The equals(..) method of HashMap has linear complexity. So does the one of ArrayList. I don't think it can get any better - you have to compare each element
You may assume the Java library objects like Long, ArrayList and String have correct implementations for equals() and hashCode().
(You are correct in that when you use an object as key in a HashMap you should make sure it has consistent implementations for those methods)
I need to create a Set of objects. The concern is I do not want to base the hashing or the equality on the objects' hashCode and equals implementation. Instead, I want the hash code and equality to be based only on each object's reference identity (i.e.: the value of the reference pointer).
I'm not sure how to do this in Java.
The reasoning behind this is my objects do not reliably implement equals or hashCode, and in this case reference identity is good enough.
I guess that java.util.IdentityHashMap is what you're looking for (note, there's no IdentityHashSet). Lookup the API documentation:
This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). (In normal Map implementations (like HashMap) two keys k1 and k2 are considered equal if and only if (k1==null ? k2==null : k1.equals(k2)).)
This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.
edit: See Joachim Sauer's comment below, it's really easy to make a Set based on a certain Map. You'd need to do something like this:
Set<E> mySet = Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
You could wrap your objects into a wrapper class which could then implement hashcode and equals based simply on the object's identity.
You can extend HashSet (or actually - AbstractSet) , and back it with IdentityHashMap which uses System.identityHashCode(object) instead of obj.hashCode().
You can simply google for IdentityHashSet, there are some implementations already. Or use Collections.newSetFromMap(..) as suggested by Joachim Sauer.
This of course should be done only if you are not in "possession" of your objects' classes. Otherwise just fix their hashCode()