Why must the hashcode method be overridden while overriding the equals method

We all know that the Java language is completely object-oriented. In java, all objects inherit from the Object class.
Its equals method compares the addresses pointed to by the references of the two objects, and hashcode is a local method that returns the object address value. There are two methods equals and hashCode in the Ojbect class, both of which are used to compare whether two objects are equal.

Why must the hashcode method be rewritten when the equals method is rewritten?

Let's take a look at a java convention:

There is a contract between equals() and hashCode() in Java:

  • 1. If two objects are equal, their hash codes must be equal;
  • 2. But if the hash codes of two objects are equal, the two objects are not necessarily equal.

It can be understood in this way: the business logic of judging the equality of objects is changed by overriding the equals method. The designer of the class does not want to compare whether two objects are equal by comparing the memory address, and the hashcode method continues to compare according to the address. Well, let's just change it along with it.

Another reason comes from collections. Let's talk slowly~

for example:

In the school, it is through the student number to determine whether it is this person or not.

The scenario in the code below is student status entry. Student ID 123 is assigned to student Tom, student ID 456 is assigned to student Jerry, and student ID 123 is assigned to Lily by mistake. In the process of enrolling students, there should be no situation where the student number is the same.

According to the needs of the situation, it is not possible to add duplicate objects, which can be achieved through HashSet.

 

public class Test {
public static void main(String[] args) {
Student stu = new Student(123,"Tom");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(new Student(456, "Jerry"));
set.add(new Student(123, "Lily"));
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student.getStuNum() + " --- " + student.getName());
}
}
};
class Student {
private int stuNum;
private String name;
public Student(int stuNum,String name){
this.stuNum = stuNum;
this.name = name;
}
public int getStuNum() {
return stuNum;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(obj instanceof Student){
if(this.getStuNum()==((Student)obj).getStuNum())
return true;
}
return false;
}
}

 The output is:

 

123 --- Lily
456 --- Jerry
123 --- Tom

According to the output, we found that assigning student number 123 to Lily again succeeded. Where is the problem?

Let's take a look at the add method of HashSet:

 

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

 In fact, HashSet is implemented through HashMap, so we trace the put method of HashMap:

 

 

public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}

 1. According to the key, that is, the object to be added to the HashSet, the hashcode is obtained, and the hash code is obtained by performing a specific bit operation on the hashcode;

 

2. Use the hash code to locate the index of the array and get the chain head of the linked list;

3. Traverse the linked list to find whether there is the same key. The judgment is based on e.hash == hash && ((k = e.key) == key || key.equals(k)). When adding Lily, because the equals method is overridden, the second condition should be true when traversing to Tom; but because the hashcode method still uses the parent class, the hashcodes of Tom and Lily are different, that is, the hash codes are different. A condition is false. The two objects obtained here are different, so the HashSet adds Lily successfully.

It is concluded that the reason is that the hashcode method has not been rewritten, so let's make a change:

public class Test {
public static void main(String[] args) {
Student stu = new Student(123,"Tom");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(new Student(456, "Jerry"));
set.add(new Student(123, "Lily"));
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student.getStuNum() + " --- " + student.getName());
}
}
};
class Student {
private int stuNum;
private String name;
public Student(int stuNum,String name){
this.stuNum = stuNum;
this.name = name;
}
public int getStuNum() {
return stuNum;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(obj instanceof Student){
if(this.getStuNum()==((Student)obj).getStuNum())
return true;
}
return false;
}
@Override
public int hashCode() {
return getStuNum();
}
}

 output:

456---Jerry
123---Tom

Override the hashcode method to return the student ID. OK, you're done.

Some people may wonder, is the condition e.hash == hash && ((k = e.key) == key || key.equals(k)) a bit complicated, I feel that only the equals method can be used. , why bother to judge the hashcode?

Because when traversing and judging in the linked list structure of HashMap, the business logic of the overridden equals method to compare whether the objects are equal in a specific case is more complicated, and the looping will affect the search efficiency. Therefore, the judgment of hashcode is put in the front here. As long as the hashcode is not equal, the game is over, and there is no need to call complex equals. Improve the efficiency of HashMap to a large extent.

So rewriting the hashcode method is to allow us to use collection classes such as HashMap normally, because HashMap judges whether objects are equal to both hashcode and equals comparison. And this implementation is to improve the efficiency of HashMap.

 

Original link: http://www.cnblogs.com/xmsx/p/5705474.html

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326696794&siteId=291194637