We all know that the Java language is completely object-oriented. In Java, all objects are inherited from the Object class.
The equals method compares the addresses pointed to by references of two objects. Hashcode is a local method, which returns the object address value. There are two methods in the Ojbect class equals and hashCode. These two methods are used to compare whether the two objects are equal.
Why do we have to override the hashcode method while rewriting the equals method?
It can be understood as follows: after rewriting the equals method, the business logic for judging the equality of objects will change. The designer of the class does not want to compare the two objects equality by comparing the memory address. It is meaningless to continue to compare according to the address. So, it is simply changed together.
Another reason comes from collections. Let's talk about it slowly below ~
For example:
In school, it is through the student ID to determine whether this person belongs to.
The scenario in the code below is student record entry, student number 123 is assigned to student Tom, student number 456 is assigned to student Jerry, and student number 123 is assigned to Lily by mistake. However, the same student number should not occur during the process of enrolling the student status.
Depending on the situational requirements, duplicate objects cannot be added, and can be implemented 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;}@Overridepublic 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
Based on the output, we found that assigning student number 123 to Lily again succeeded. What went wrong?
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 by HashSet, get the hashcode, and the hashcode is used to perform specific bit operations to obtain the hash code;
2. Use hash code positioning to find the array subscript to get the link header of the linked list;
3. Traverse the linked list to find out if there is the same key. The basis for judgment is e.hash == hash && ((k = e.key) == key || key.equals(k)). When adding Lily, because the equals method is rewritten, the second condition should be true when traversing Tom; but because the hashcode method still uses the parent class, the hashcode of Tom and Lily is different, that is, the hash code is different, and the first condition is false. Here we get that the two objects are different so HashSet adds Lily successfully.
The reason is that the hashcode method is not rewriting. Here is a modification:
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;}@Overridepublic boolean equals(Object obj) {if(this==obj)return true;if(obj instanceof Student){if(this.getStuNum()==((Student)obj).getStuNum())return true;}return false;}@Overridepublic int hashCode() {return getStuNum();}} Output:
456 --- Jerry
123 --- Tom
Rewrite the hashcode method and return the student number. OK, it's done.
Some people may wonder, e.hash == hash && ((k = e.key) == key || key.equals(k)) is a bit complicated? I think it's enough to just use the equals method. Why do you need to judge hashcode in one go?
Because when traversing and judging in the linked list structure of HashMap, the business logic of rewritten equals method is more complicated to compare whether the objects are equal in specific situations, and the business logic of looping down will affect the search efficiency. So here we put the judgment of hashcode first. As long as the hashcode is not equal, you will finish playing, and there is no need to call complex equals anymore. Improve the efficiency of HashMap to a large extent.
Therefore, the hashcode method is to allow us to use collection classes such as HashMap normally, because HashMap determines whether objects are equal, both hashcode and equals comparison. This implementation is to improve the efficiency of HashMap.