Why is there a need to override hashcode if I override the "equals" method in Java?

I know it is necessary to override hashcode whenever the equals method is overridden in Java. This is just a contract. I am trying to understand the logic of this. I read * Effective Java by Joshua Bloch , and I came across this code (paragraph 9, p. 45):

 import java.util.HashMap; import java.util.Map; public final class PhoneNumber { private final short areaCode; private final short prefix; private final short lineNumber; public PhoneNumber(int areaCode, int prefix, int lineNumber) { rangeCheck(areaCode, 999, "area code"); rangeCheck(prefix, 999, "prefix"); rangeCheck(lineNumber, 9999, "line number"); this.areaCode = (short) areaCode; this.prefix = (short) prefix; this.lineNumber = (short) lineNumber; } private static void rangeCheck(int arg, int max, String name) { if (arg < 0 || arg > max) throw new IllegalArgumentException(name + ": " + arg); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof PhoneNumber)) return false; PhoneNumber pn = (PhoneNumber) o; return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode; } // Broken - no hashCode method! // A decent hashCode method - Page 48 // @Override public int hashCode() { // int result = 17; // result = 31 * result + areaCode; // result = 31 * result + prefix; // result = 31 * result + lineNumber; // return result; // } // Lazily initialized, cached hashCode - Page 49 // private volatile int hashCode; // (See Item 71) // // @Override public int hashCode() { // int result = hashCode; // if (result == 0) { // result = 17; // result = 31 * result + areaCode; // result = 31 * result + prefix; // result = 31 * result + lineNumber; // hashCode = result; // } // return result; // } public static void main(String[] args) { Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); System.out.println(m.get(new PhoneNumber(707, 867, 5309))); } } 

This is what he mentions in the text, with which I find it difficult to understand.

At this point, you can expect m.get(new PhoneNumber(707, 867, 5309)) return "Jenny" but return null. Note that two PhoneNumber instances are used: one is used to insert into the HashMap and the second, an equal instance is used to (attempt) a search. A PhoneNumber class error to override the hashCode cause two equal instances have unequal hash codes, in violation of the hashcode contract. Therefore, the get method is likely to look up the phone number in another hash bucket from where it was stored by the put method

I do not understand what two instances of PhoneNumber he is talking about. There is only an instance that I create in m.put(new PhoneNumber(707, 867, 5309), "Jenny") . Also, I'm looking again for this object, which should return the same hash code, even if it inherits the hashCode method from the object class. Why is this happening? Some explanation here will help a lot.

+4
source share
6 answers

I do not understand what two instances of PhoneNumber he is talking about.

The first is the one you used to insert.

 m.put(new PhoneNumber(707, 867, 5309), "Jenny")); 

The second instance is the one used for extraction.

 m.get(new PhoneNumber(707, 867, 5309)); // Notice the use of "new" 

Also, I'm looking again for this object, which should return the same hash code, even if it inherits the hashCode method from the object class.

This is not true. By default, the implementation of the hashCode() class in Object returns separate integers for different objects, because it is implemented by converting the internal address of the object to an integer. Therefore, hash verification is not performed.

If, on the other hand, you tried to retrieve PhoneNumber using the same instance

 PhoneNumber phoneNum = new PhoneNumber(707, 867, 5309); m.put(phoneNum, "Jenny")); m.get(phoneNum); // Not NULL 

Checking the hash code will pass (since you used the same object that was inserted before) and equals() . This, of course, is not recommended, because we are much more likely to use a different key object than the one used for insertion.

The key used, however, will have a meaningful equivalent (for example, another String object, but whose text is the same), and therefore, the implementation of hashCode() necessary for proper matching and searching.

Also see: Checking and deleting elements in Java HashMap

+4
source

If you do not redefine hashcode with equals , then each instance, for example. " new PhoneNumber(707, 867, 5309)" will have a different hash code.

So, from the point of view of HashMap, they will be considered as two different entries. Just read more about how hashmap works. Therefore, if two objects, which may be equal but have a different code, will be stored in different codes.

+3
source

Go to this

A hash code is used to maintain the contract and to uniquely identify each object in a hashmap or hash table.

0
source

According to your question.

Also, I'm looking again for this object, which should return the same hash code, even if it inherits the hashCode method from the object class.

Check the hashCode documentation of the # object here

As far as reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects . (This is usually done by converting the internal address of the object to an integer, but this implementation method is not required by the JavaTM programming language.)

In other words, hashCode will not return the same integer for two objects unless you overwrite it.

0
source

You think that you have only one instance, but you have two instances . Everyone

new PhoneNumber (707, 867, 5309)

creates an instance in another memory location. The m.get hash map method looks for a new instance that you create in the method call. This instance has a different hash code for the first instance created in the m.put method.

(Since there are two instances of the object, Java will compute another hashCode in the superclass object.)

Thus, the hash map cannot find the first object with the hash code of the second object.

Please store the phone number in a variable and use the variable to set and get it to work - because it is the same object in the same memory location with the same hash code as equals == true.

 public static void main(String[] args) { Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>(); PhoneNumber num = new PhoneNumber(707, 867, 5309); m.put(num, "Jenny"); System.out.println(m.get(num)); } 

But for real use, you must correctly implement the hashCode and equals methods.

0
source

The goal of hashCode() is to quickly identify objects to which the object is not equal; one hashCode() call will instantly show that the object is not equal to any object whose hashCode() method was called, and returned a different value. This is a pretty powerful feature, but it requires all two β€œequal” objects to return the same hashCode value. If two objects do not return the same hashCode value, some collection types assume that they cannot be equal and will not bother calling equals to find out if they can be.

0
source

Source: https://habr.com/ru/post/1498697/


All Articles