HashMap Not Calling Object.equals? - java

I wrote a class that overrides the equals(Object) method in class Object to compare objects of the class type to other objects of a class type using the object's instance values.
When I put an instance of the object in a HashMap as the key, and then call get(Object) on the map with a new but identical object as the key, it returns null.
I've tried passing a new, identical object to the equals method and it returns true, so the problem isn't my comparison code.
From what I've gathered through debugging, the equals(Object) method in my object is never called.
But if you use a String key in a HashMap and then pass a new instance with identical characters to get(Object), it returns the value successfully.
Why is this happening? What do I have to do to have HashMap test keys based on MY equals method?

You need to also override Object.hashcode(). Take a look at the link, as it specifies that hashcode() and equals() have a contract to ensure proper functionality in HashTable's, HashMap's, and HashSet's.
In a HashMap, values are stored in buckets, which are reached by the hashcode of the key. Once the proper bucket is found, the equals method is then applied to each member of the bucket until equality is determined. Because of this, it is important to make sure that your hash algorithm 'hashes well'.

You should also override hashCode otherwise it won't work as HashMap (as the name suggests) equates equality based on a collection of hashes. The Java "honour code" for overriding equals is that you should also override hashCode at the same time.

Related

If the hashcode() creates hashcode based on the address of the object, how can two different objects with same contents create the same hashcode?

When you read the description of hashCode() in the Object class, it says that
If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
I've read an article and it says that an object's hashcode() can provide different integer values if it runs in different environments even though their content is the same.
It does not happen with String class's hashcode() because the String class's hashcode() creates integer value based on the content of the object. The same content always creates the same hash value.
However, it happens if the hash value of the object is calculated based on its address in the memory. And the author of the article says that the Object class calculates the hash value of the object based on its address in the memory.
In the example below, the objects, p1 and p2 have the same content so equals() on them returns true.
However, how come these two objects return the same hash value when their memory addresses are different?
Here is the example code: main()
Person p1 = new Person2("David", 10);
Person p2 = new Person2("David", 10);
boolean b = p1.equals(p2);
int hashCode1 = p1.hashCode();
int hashCode2 = p2.hashCode();
Here is the overriden hashcode()
public int hashCode(){
return Objects.hash(name, age);
}
Is the article's content wrong?
If there is a hashCode() that calculates a hash value based on the instance's address what is the purpose of them?
Also, if it really exists, it violates the condition that Object class's hashCode() specifies. How should we use the hashCode() then?
I think you have misunderstood this statement:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
The word “must” means that you, the programmer, are required to write a hashCode() method which always produces the same hashCode value for two objects which are equal to each other according to their equals(Object) method.
If you don’t, you have written a broken object that will not work with any class that uses hash codes, particularly unsorted Sets and Maps.
I've read an article and it says that an object's hashcode() can provide different integer values if it runs in different environments even though their content is the same.
Yes, hashCode() can provide different values in different runtimes, for a class which does not override the hashCode() method.
… how come these two objects return the same hash value when their memory addresses are different?
Because you told them to. You overrode the hashCode() method.
If there is a hashCode() that calculates a hash value based on the instance's address what is the purpose of them?
It doesn’t have a lot of use. That’s why programmers are strongly recommended to override the method, unless they don’t care about object identity.
Also, if it really exists, it violates the condition that Object class's hashCode() specifies. How should we use the hashCode() then?
No it does not. The contract states:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
As long as the hashCode method defined in the Object class returns the same value for the duration of the Java runtime, it is compliant.
The contract also says:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
Two objects whose equals method reports that they are effectively equal must return the same hashCode. Any instance of the Object class whose type is not a subclass of Object is only equal to itself and cannot be equal to any other object, so any value it returns is valid, as long as it is consistent throughout the life of the Java runtime.
The article is wrong, memory address is not involved (btw. the address can change during the lifecycle of objects, so it's abstracted away as a reference). You can look at the default hashCode as a method that returns a random integer, which is always the same for the same object.
Default equals (inherited from Object) works exactly like ==. Since there is a condition (required for classes like HashSet etc. to work) that states when a.equals(b) then a.hashCode == b.hashCode, then we need a default hashCode which only has to have one property: when we call it twice, it must return the same number.
The default hashCode exists exactly so that the condition you mention is upheld. The value returned is not important, it's only important that it never changes.
No p1 and p2 does not have the same content
If you do p1.equals(p2) that will be false, so not the same content.
If you want p1 and p2 to equal, you need to implement the equals methods from object, in a way that compare their content. And IF you implement the equals method, then you also MUST implement the hashCode method, so that if equals return true, then the objects have the same hashCode().
Here's the design decision every programmer needs to make for objects they define of (say) MyClass:
Do you want it to be possible for two different objects to be "equal"?
If you do, then firstly you have to implement MyClass.equals() so that it gives the correct notion of equality for your purposes. That's entirely in your hands.
Then you're supposed to implement hashCode such that, if A.equals(B), then A.hashCode() == B.hashCode(). You explicitly do not want to use Object.hashCode().
If you don't want different objects to ever be equal, then don't implement equals() or hashCode(), use the implementations that Object gives you. For Object A and Object B (different Objects, and not subclasses of Object), then it is never the case that A.equals(B), and so it's perfectly ok that A.hashCode() is never the same as B.hashCode().

why should I override equals and hashcode method for following scnerio

why I need to override for direct access of value in Hash map.That is if insert data into hashmap as follow HashMap,I could get value by giving the Key as Integer ,would get Object as Value.In this case is it necessary to Override equals() and hashCode() method?Please give suggestion.
No, you don't need to override anything to use an object as a value in a HashMap.
Only keys need to have a working hashCode().
However, you need to implement these two methods (technically only equals, but these two are a set, really) if you want to use things like Map#containsValue, List#indexOf or Collection#contains (and these should not just be using reference identity).
hashCode() is used to search for a specific elem when you want to retrieve it from a hashTable. hashCode() doesn't have to be distinct. in fact, you could just return the same integer for all your instance, but then, elems are stored in a list instead of a hashTable, and will cause a performance problem.
By default implementation of hashCode() (which is the implementation of Object for subClass to extents from )of JVM returns a integer according to the memory address of the object, so this should be enough, but this implement was not required by the JVM standard.
By default(Class object), implementation of equals() will return true and only return true when they have same reference , ie obj1 == obj2. read this
keep in mind that:
equal objects must have same hashCode()
those have same hashCode() are not required to be equal to each other.
I think override of hashCode() is not needed in most situations(not extends from other Class), cause modern JVMs has done
pretty good job for you.
So conclusion is:
if your super class have overwrite the hashCode() and equals() method, then you should override them, or at least take a look at the implementation, and decide whether you should override them.

Questions about contains method in Hash Set

I am a high school student so I apologize for the terms I may misuse.
So I am making a slide puzzle game and working on the AI part. So I have a constructor that construct board and assign its hashcode such as 123456780. In my A* algorithm, I compare if the board that I generate (to find the solution) is already in the hashset. So I use contains method for it right? but how does the contains method works to check if the two boards are identical?.
public Board()
{
board = new int [3][3];
setPieces (board);
hashCode = generateHashCode ();
}
This is one of my constructor. In my board object, I have 2D array and the hashcode. But I wonder again, if the built-in contains method in Hash Set compare two boards hashcode. Or I need to write one.
Also, when I assign the hash code to a board, I should do it in my constructor right?
Thanks you
As you've discovered, you need to return a hashcode for your object in the overridden hashCode() method.
You can either compute a hashcode in that method, or compute it in the ctor and store it in a field, then return the field in the method override.
Any collection that uses the 'Hash' prefix has all objects contained within the collection stored in groups by their hashcode. So when you call a method like contains() the collection loops through each hashcode group and checks whether the hashcode of the object you parsed in matches the group. When a match is found, the objects within that hashcode group are checked using the equals() method of the stored object until an Object's equals() method returns true.
If you do not override the hashcode() method, then each Object subclass is given a more or less unique hashcode by the Object class implementation of the hashcode() method. This can cause some problems if you have already overridden the equals() method, because if two objects are considered equal the contains() method might still not find it if the hashcodes do not match.
The hashcode contract:
When implementing hashcode() the Java Object API has laid out a contract or a set of specifications on how you should implement the hashcode method. And they are;
If hashcode() is run on an object multiple times, it must always return the same number.
If two objects are considered equal by the equals() method then their hashcode() methods must return the same value.
If two objects have the same hashcode, they do not have to be considered equal by their equals() methods.
References:
The hashcode contract.
Implementing hashcode.

Relationship between hashCode and equals method in Java [duplicate]

This question already has answers here:
What issues should be considered when overriding equals and hashCode in Java?
(11 answers)
Why do I need to override the equals and hashCode methods in Java?
(31 answers)
Closed 9 years ago.
I read in many places saying while override equals method in Java, should override hashCode method too, otherwise it is "violating the contract".
But so far I haven't faced any problem if I override only equals method, but not hashCode method.
What is the contract? And why am I not facing any problem when I am violating the contract? In which case will I face a problem if I haven't overridden the hashCode method?
The problem you will have is with collections where unicity of elements is calculated according to both .equals() and .hashCode(), for instance keys in a HashMap.
As its name implies, it relies on hash tables, and hash buckets are a function of the object's .hashCode().
If you have two objects which are .equals(), but have different hash codes, you lose!
The part of the contract here which is important is: objects which are .equals() MUST have the same .hashCode().
This is all documented in the javadoc for Object. And Joshua Bloch says you must do it in Effective Java. Enough said.
According to the doc, the default implementation of hashCode will return some integer that differ for every object
As much as is reasonably practical, the hashCode method defined by class Object does
return distinct integers for distinct objects. (This is typically implemented by
converting the internal address of the object into an integer, but this implementation
technique is not required by the JavaTM programming language.)
However some time you want the hash code to be the same for different object that have the same meaning. For example
Student s1 = new Student("John", 18);
Student s2 = new Student("John", 18);
s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode
This kind of problem will be occur if you use a hash data structure in the collection framework such as HashTable, HashSet. Especially with collection such as HashSet you will end up having duplicate element and violate the Set contract.
Yes, it should be overridden. If you think you need to override equals(), then you need to override hashCode() and vice versa. The general contract of hashCode() is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
The contract is that if obj1.equals(obj2) then obj1.hashCode() == obj2.hashCode() , it is mainly for performance reasons, as maps are mainly using hashCode method to compare entries keys.
Have a look at Hashtables, Hashmaps, HashSets and so forth. They all store the hashed key as their keys. When invoking get(Object key) the hash of the parameter is generated and lookup in the given hashes.
When not overwriting hashCode() and the instance of the key has been changed (for example a simple string that doesn't matter at all), the hashCode() could result in 2 different hashcodes for the same object, resulting in not finding your given key in map.get().
See JavaDoc of java.lang.Object
In hashCode() it says:
If two objects are equal according to the equals(Object) method,
then calling the hashCode method on each of the two objects must
produce the same integer result.
(Emphasis by me).
If you only override equals() and not hashCode() your class violates this contract.
This is also said in the JavaDoc of the equals() method:
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
A contract is: If two objects are equal then they should have the same hashcode and if two objects are not equal then they may or may not have same hash code.
Try using your object as key in HashMap (edited after comment from joachim-sauer), and you will start facing trouble. A contract is a guideline, not something forced upon you.

Using HashMap with custom key

Quick Question: If I want to use HashMap with a custom class as the key, must I override the hashCode function? How will it work if I do not override that function?
If you don't override hashCode AND equals you will get the default behaviour which is that each object is different, regardless of its contents.
Technically, you don't have to override the hashCode method as long as equal objects have the same hashCode.
So, if you use the default behaviour defined by Object, where equals only returns true only for the same instance, then you don't have to override the hashCode method.
But if you don't override the equals and the hashCode methods, it means you have to make sure you're always using the same key instance.
E.g.:
MyKey key1_1 = new MyKey("key1");
myMap.put(key1_1,someValue); // OK
someValue = myMap.get(key1_1); // returns the correct value, since the same key instance has been used;
MyKey key1_2 = new MaKey("key1"); // different key instance
someValue = myMap.get(key1_2); // returns null, because key1_2 has a different hashCode than key1_1 and key1_1.equals(key1_2) == false
In practice you often have only one instance of the key, so technically you don't have to override the equals and hashCode methods.
But it's best practice to override the equals and hashCode methods for classes used as keys anyway, because sometime later you or another developer might forget that the same instance has to be used, which can lead to hard to track issues.
And note: even if you override the equals and hashCode methods, you must make sure you don't change the key object in a way that would change the result of the equals or the hashCode methods, otherwise the map won't find your value anymore. That's why it's recommended to use immutable objects as keys if possible.
The only time you don't have to override the hashCode() function is when you also don't override equals, so you use the default Object.equals definition of reference equality. This may or may not be what you want -- in particular, different objects will not be considered equal even if they have the same field values.
If you override equals but not hashCode, HashMap behavior will be undefined (read: it won't make any sense at all, and will be totally corrupted).
It depends on the object class you are using as a key. If it's a custom class like you propose, and it doesn't extend anything (i.e. it extends Object) then the hashCode function will be that of Object, and that will consider memory references, making two objects that look the same to you hash to different codes.
So yes, unless you are extending a class with a hashCode() function you know works for you, you need to implement your own. Also make sure to implement equals(): some classes like ArrayList will only use equals while others like HashMap will check on both hashCode() and equals().
Consider also that if your key is not immutable you may have problems. If you put an entry with a mutable key in the map an you change later the key in a way that it affects hashcode and equals you may lose your entry in the map,as you won't be able to retrieve it anymore.
You should override the equals() and hashCode() methods from the Object class. The default implementation of the equals() and hashcode(), which are inherited from the java.lang.Object uses an object instance’s memory location (e.g. MyObject#6c60f2ea). This can cause problems when two instances of the an objects have the same properties but the inherited equals() will return false because it uses the memory location, which is different for the two instances.
Also the toString() method can be overridden to provide a proper string representation of your object.
primary considerations when implementing a user defined key
If a class overrides equals(), it must override hashCode().
If 2 objects are equal, then their hashCode values must be equal as well.
If a field is not used in equals(), then it must not be used in hashCode().
If it is accessed often, hashCode() is a candidate for caching to enhance performance.

Categories