catalogue
1. Start with the Object class source code
2. Why does overriding the equals method have to override the hashCode method?
1. Start with the Object class source code
Both equals and hashcode are methods of Object objects. Therefore, all Java classes inherit these two methods by default. Let's first look at the source code of the Object class:
/** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. * <p> * The general contract of {@code hashCode} is: * <ul> * <li>Whenever it is invoked on the same object more than once during * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, provided no information * used in {@code 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. * <li>If two objects are equal according to the {@code equals(Object)} * method, then calling the {@code hashCode} method on each of * the two objects must produce the same integer result. * <li>It is <em>not</em> required that if two objects are unequal * according to the {@link java.lang.Object#equals(java.lang.Object)} * method, then calling the {@code 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 hash tables. * </ul> * <p> * As much as is reasonably practical, the hashCode method defined by * class {@code 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 * Java™ programming language.) * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ 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); }
hashCode: a native method that returns the memory address of the object,
equals: for basic data types, = = compares the values of two variables. For reference objects, = = compares the addresses of two objects.
Next, let's look at the comments of hashCode. The source code gives some general protocols:
1.stay Java Multiple calls to the same object during application execution hashCode Method must consistently return the same integer, provided that the object is equals The information used in the comparison has not been modified. This integer does not need to be consistent from one execution of an application to another execution of the same application. 2.If according to equals(Object) Method, if the two objects are equal, call on each of the two objects hashCode Methods must produce the same integer result. 3.If according to equals(java.lang.Object) Method, if two objects are not equal, then two objects do not necessarily have to produce different integer results. However, programmers should be aware that generating different integer results for unequal objects can improve the performance of hash tables.
In other words, these two methods have the following characteristics:
(1) If two objects are the same (i.e. true is returned by equals comparison), their hashCode values must be the same;
(2) If two objects are different (i.e. false is returned by equals comparison), their hashCode values may be the same or different;
(3) If the hashcodes of two objects are the same (there is hash conflict), they may be the same or different (that is, the equals comparison may be false or true);
(4) If the hashcodes of the two objects are different, they must be different (that is, the equals comparison returns false);
2. Why does overriding the equals method have to override the hashCode method?
Hashcode is used for fast access to hash data. For example, when using HashSet/HashMap/Hashtable classes to store data, whether it is the same or not is judged according to the hashcode value of the storage object.
If we rewrite the equals method of an object instead of hashcode, when we create a new object again, the equals method returns true, but the hashcode method returns different.
If you need to store these objects in a combination (such as Set, Map...), it violates the principle of the original Set. Let's take a look through a piece of code.
/** * @see Person * @param args */ public static void main(String[] args) { HashMap<Person, Integer> map = new HashMap<Person, Integer>(); Person p = new Person("jack",22,"male"); Person p1 = new Person("jack",22,"male"); System.out.println("p of hashCode:"+p.hashCode()); System.out.println("p1 of hashCode:"+p1.hashCode()); System.out.println(p.equals(p1)); System.out.println(p == p1); map.put(p,888); map.put(p1,888); map.forEach((key,val)->{ System.out.println(key); System.out.println(val); }); }
(1) The of the equals and hashCode methods are not overridden
public class Person { private String name; private int age; private String sex; Person(String name,int age,String sex){ this.name = name; this.age = age; this.sex = sex; } }
The output is as follows:
p of hashCode:356573597 p1 of hashCode:1735600054 false false com.blueskyli.practice.Person@677327b6 888 com.blueskyli.practice.Person@1540e19d 888
(2) Override only the equals method
public class Person { private String name; private int age; private String sex; Person(String name,int age,String sex){ this.name = name; this.age = age; this.sex = sex; } @Override public boolean equals(Object obj) { if(obj instanceof Person){ Person person = (Person)obj; return name.equals(person.name); } return super.equals(obj); } }
The output is as follows:
p of hashCode:356573597 p1 of hashCode:1735600054 true false com.blueskyli.practice.Person@677327b6 888 com.blueskyli.practice.Person@1540e19d 888
(3) Both the equals and hashCode methods are overridden
public class Person { private String name; private int age; private String sex; Person(String name,int age,String sex){ this.name = name; this.age = age; this.sex = sex; } @Override public boolean equals(Object obj) { if(obj instanceof Person){ Person person = (Person)obj; return name.equals(person.name); } return super.equals(obj); } @Override public int hashCode() { return name.hashCode(); } }
The output is as follows:
p of hashCode:3254239 p1 of hashCode:3254239 true false com.blueskyli.practice.Person@31a7df 888
We know that the same key is not allowed in a map. As can be seen from the above code, if the equals and hashCode methods are not rewritten, different results will appear when using the map. The reasons are as follows:
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); //Here, the hash value is used to locate the approximate storage location of the object int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //In the if statement, compare hashcode first, and then call equals() to compare //Because "& &" has the function of short circuit, there is no need to call the equals method as long as the hashcode is different 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; }
3. Summary
Overriding the equals() method overrides the hashCode() method. The hashcode method is rewritten to compare the data stored in the HashSet / HashMap / Hashtable class, so as to avoid different results than expected.