Override hashcode and equals methods

One. preface

We all know that to compare whether two objects are equal, we need to call the equals() method of the object, that is, to judge whether the object address pointed to by the object reference is equal. When the object address is equal, the object handle, object header, object instance data and object type data related to the object are also completely consistent, So we can compare the addresses of objects to determine whether they are equal.

II. Object source code understanding

Objects use the equals method and hashcode method of Object without rewriting. From the source code of Object class, we know that the default equals determines whether the reference of two objects points to the same Object; Hashcode also generates an integer value according to the Object address;

In addition, we can see that the modifier of the hashcode() method of Object is native, indicating whether the method is implemented by the operating system. java calls the underlying code of the operating system to obtain the hash value.

public class Object {
 
public native int hashCode();
 
    /**
     * Indicates whether some other object is "equal to" this one.
     * <p>
     * The {@code equals} method implements an equivalence relation
     * on non-null object references:
     * <ul>
     * <li>It is <i>reflexive</i>: for any non-null reference value
     *     {@code x}, {@code x.equals(x)} should return
     *     {@code true}.
     * <li>It is <i>symmetric</i>: for any non-null reference values
     *     {@code x} and {@code y}, {@code x.equals(y)}
     *     should return {@code true} if and only if
     *     {@code y.equals(x)} returns {@code true}.
     * <li>It is <i>transitive</i>: for any non-null reference values
     *     {@code x}, {@code y}, and {@code z}, if
     *     {@code x.equals(y)} returns {@code true} and
     *     {@code y.equals(z)} returns {@code true}, then
     *     {@code x.equals(z)} should return {@code true}.
     * <li>It is <i>consistent</i>: for any non-null reference values
     *     {@code x} and {@code y}, multiple invocations of
     *     {@code x.equals(y)} consistently return {@code true}
     *     or consistently return {@code false}, provided no
     *     information used in {@code equals} comparisons on the
     *     objects is modified.
     * <li>For any non-null reference value {@code x},
     *     {@code x.equals(null)} should return {@code false}.
     * </ul>
     * <p>
     * The {@code equals} method for class {@code Object} implements
     * the most discriminating possible equivalence relation on objects;
     * that is, for any non-null reference values {@code x} and
     * {@code y}, this method returns {@code true} if and only
     * if {@code x} and {@code y} refer to the same object
     * ({@code x == y} has the value {@code true}).
     * <p>
     * Note that it is generally necessary to override the {@code hashCode}
     * method whenever this method is overridden, so as to maintain the
     * general contract for the {@code hashCode} method, which states
     * that equal objects must have equal hash codes.
     *
     * @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is the same as the obj
     *          argument; {@code false} otherwise.
     * @see     #hashCode()
     * @see     java.util.HashMap
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }
}

Three. The scenario where equals() needs to be rewritten

Suppose there are many student objects. By default, to judge whether multiple student objects are equal, you need to judge according to the address. If the Object addresses are equal, the instance data of the Object must be the same. But now we stipulate that when the student's name, age and gender are equal, the student objects are considered to be equal, and the Object addresses need not be exactly the same, For example, the address of student a is 100, the personal information of student a is (Name: A, gender: female, age: 18, address: No. 999, software Road, Beijing, weight: 48), the address of student a is 388, and the personal information of student a is (name: A, gender: female, age: 18, address: No. 888, Baofu Road, Guangzhou, weight: 55), At this time, if we don't rewrite the equals method of Object, the return must be false and unequal. At this time, we need to rewrite the equals() method according to our own needs.

package jianlejun.study;
 
public class Student {
	private String name;// full name
	private String sex;// Gender
	private String age;// Age
	private float weight;// weight
	private String addr;// address
	
	// Override hashcode method
	@Override
	public int hashCode() {
		int result = name.hashCode();
		result = 17 * result + sex.hashCode();
		result = 17 * result + age.hashCode();
		return result;
	}
 
	// Override the equals method
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Student)) {
       // instanceof has handled the case of obj = null
			return false;
		}
		Student stuObj = (Student) obj;
		// Address equality
		if (this == stuObj) {
			return true;
		}
		// If two objects have the same name, age and gender, we think two objects are equal
		if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.age.equals(this.age)) {
			return true;
		} else {
			return false;
		}
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getSex() {
		return sex;
	}
 
	public void setSex(String sex) {
		this.sex = sex;
	}
 
	public String getAge() {
		return age;
	}
 
	public void setAge(String age) {
		this.age = age;
	}
 
	public float getWeight() {
		return weight;
	}
 
	public void setWeight(float weight) {
		this.weight = weight;
	}
 
	public String getAddr() {
		return addr;
	}
 
	public void setAddr(String addr) {
		this.addr = addr;
	}
 
}

Now let's write an example to test the results:

public static void main(String[] args) {
	Student s1 =new Student();
	s1.setAddr("1111");
	s1.setAge("20");
	s1.setName("allan");
	s1.setSex("male");
	s1.setWeight(60f);
	Student s2 =new Student();
	s2.setAddr("222");
	s2.setAge("20");
	s2.setName("allan");
	s2.setSex("male");
	s2.setWeight(70f);
	if(s1.equals(s2)) {
		System.out.println("s1==s2");
	}else {
		System.out.println("s1 != s2");
	}
}

After rewriting the equals method of student, s1 == s2 will be output here, which meets our requirements. If the equals method is not rewritten, the previous code must output S1= s2.

Through the above example, do you think that you should rewrite the equals method and hashcode method of Object at the same time? How can the above example only use the equals method? The hashcode method is not reflected. Don't worry. Let's look down.

Four. Scenarios where hashcode() needs to be rewritten

Based on the above example, student1 and student2 are considered equal after overriding the equals method.

In the case of two objects equal, put them into Map and Set respectively

Add the following code to the above code:

Set set = new HashSet();
	set.add(s1);
	set.add(s2);
	System.out.println(set);

If the hashcode() method of the Object is not overridden (that is, the hashcode method block in the student class above is removed), the output will be displayed here

[jianlejun.study.Student@7852e922, jianlejun.study.Student@4e25154f]

Description the set container class has 2 elements Wait, why are there two elements???? After the test just now, isn't s1 equal to s2? There will be a de duplication operation according to the characteristics of the set container. Why are there two elements now. This involves the underlying implementation of set. Here is a brief introduction that the underlying of HashSet is implemented through HashMap. Finally, whether the elements in the set container are equal is determined by comparing the hashcode of the object. Now you can try the hashcode method commented out just now, get it back, and then run it again to see if it's amazing to output only one element

@Override
	public int hashCode() {
		int result = name.hashCode();
		result = 17 * result + sex.hashCode();
		result = 17 * result + age.hashCode();
		return result;
	}

Maybe you have a question? How to understand the code in hashcode? How to write? In fact, there is a relatively fixed writing method. First sort out the attributes you judge the equality of objects, and then take a positive integer as small as possible (as small as possible, I'm afraid the final result will exceed the retrieval range of integer int). Here I take 17 (it seems that 17 has been used in the JDK source code), then calculate the hashcode of 17 * attribute + hashcode of other attributes, and repeat the steps.

After rewriting the hashcode method, the output result is:

[jianlejun.study.Student@43c2ce69]

Similarly, you can put it into the HashMap under test. The key is < S1, S1 >, < S2, S2 >, and the map also regards two identical objects as different keys (the key of the map is not allowed to be repeated, and the same key will be overwritten). Then there will be two elements in the map without rewriting. In the case of rewriting, the last input element will overwrite the previous value

Map m = new HashMap();
	m.put(s1, s1);
	m.put(s2, s2);
	System.out.println(m);
	System.out.println(((Student)m.get(s1)).getAddr());
 
Output result:
{jianlejun.study.Student@43c2ce69=jianlejun.study.Student@43c2ce69}
222

You can see that the final output address information is 222222, which is the value of s2 member variable addr. Tomorrow, s2 has replaced the value value with s1 in the map, and the final result is map < s1, s2 >. That is, key is s1value is s2

Five. Principle analysis

Because we did not override the hashcode method of the parent class (Object), the hashcode method of Object will generate the corresponding hashcode according to the addresses of the two objects;

s1 and s2 are new respectively, so their addresses must be different, and naturally the hashcode value will be different.

The only criterion that sets whether objects are distinguished is whether two objects have the same hashcode, and then determine whether the two objects are equal;

Map first allocates and obtains objects according to the hashcode of the Key value, saves the array subscripts, and then distinguishes unique values according to equals (see map analysis below for details)

Six. Supplement HashMap knowledge

hashMap composition structure: hashMap is composed of array and linked list;

hashMap storage: the location of an object stored in the hashMap is determined by the hashcode value of its key; Check hashMap to find the key: when finding the key, hashMap will first locate the position of the array according to the hashcode of the key value through the remainder algorithm, and then match the same key value according to the equals method of the key to obtain the corresponding object;

Case:

(1) hashmap storage

Save value rule: the index of the location of the Key stored in the array is obtained by taking the balance between the hashCode of the Key and the capacity of the HashMap (the location of the source code positioning Key stored in the array is obtained by the hashCode & (HashMap capacity-1) algorithm). This method is used here for convenience of understanding;

//For the convenience of demonstration, define a hashMap with a capacity of 3 (its default is 16)

HashMap map=newHashMap(3);

map.put(“a”,1); The hashcode value with key "a" is 97, and then the remaining 97% 3 is obtained according to the value and the hashMap capacity to get the storage bit into the array, and the subscript is 1;

map.put(“b”,2); The hashcode value with key "B" is 98,98%3 to the storage bit and the index of the array is 2;

map.put(“c”,3); The hashcode value with key "C" is 99, 99% 3 to the storage bit to the array, and the subscript is 0;

map.put(“d”,4); The hashcode value with key "d" is 100100% 3 to the storage bit to the array, and the subscript is 1;

map.put(“e”,5); Get the hashcode value with key "e" of 101101% 3 to the storage bit to the array subscript of 2;

map.put(“f”,6); The hashcode value with key "F" is 102102%3 to the storage bit and the index of the array is 0;


(2) Search key of hashmap

Get the position of the key in the array: according to the above figure, when we get the object whose key is "a", we first get the hashcode97%3 of the key and get the storage bit to the array subscript 1;

Match to get the corresponding key value object: get the data "a" and "c" objects in the following table of the array, and then according to the key Equals() to match and obtain the data object of the corresponding key;

Hashcode for HashMapde: if there is no hashcode, it means that there is no rule to find when HashMap is stored, so whenever we map When using the get () method, you need to take out the objects in the map one by one for equals matching. Will the efficiency be super slow;

hashcode method document description

On the premise that the equals method is not modified, the value returned by calling the hashcode method of the same object multiple times must be the same integer;

If two objects are equal to each other, the hashcode values of the two objects must be equal;

Generating different hashcode s for different objects can improve the performance of hash tables;

Keywords: Java HashMap hashcode equals

Added by ProXy_ on Tue, 08 Feb 2022 01:18:27 +0200