
Reading this article will take approximately 5 minutes.
Source: github.com/feigeswjtu/java-basics/edit/master/sourceCode
Background
-
Whenever equals is overridden, hashCode must also be overridden. -
Since Set stores unique objects and uses hashCode and equals for comparison, objects stored in Set must override these two methods. -
If a custom object is used as a key in a Map, hashCode and equals must be overridden.
String’s hashCode()
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
-
The importance of caching; the value attribute serves as cache here. -
In most cases, the cache is generated during use. -
When calculating the hash, a class’s own attribute values can be multiplied by 31.
-
31 is a prime number, and the property of prime numbers is that if I multiply a number by this prime, the resulting value can only be divided by the prime itself, the multiplicand, and 1. -
31 can be represented as i*31 == (i<<5)-1
, and many virtual machines have optimizations related to this. -
When choosing coefficients, one should choose as large a coefficient as possible. The larger the calculated hash address, the fewer so-called “collisions” will occur, thus improving search efficiency. -
Moreover, 31 only occupies 5 bits, reducing the probability of data overflow.
String’s equals()
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Whenever equals is overridden, hashCode must also be overridden
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(o instanceof Person){
Person p = (Person)o;
return this.age == p.getAge() && this.name.equals(p.getName());
}
return false;
}
}
public class Application {
public static void main(String[]args){
Set<Person> set = new HashSet<>();
Person p1 = new Person("Lilei", 25);
Person p2 = new Person("Lilei", 25);
set.add(p1);
System.out.println("p1 equals p2: " + (p1 == p2));//1
System.out.println("set contains p1: " + set.contains(p1));//2
System.out.println("set contains p2: " + set.contains(p2));//3
}
}
p1 equals p2: false
set contains p1: true
set contains p2: false
Because Set stores unique objects, and uses hashCode and equals for comparison, objects stored in Set must override these two methods
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Application {
public static void main(String[]args){
Set<Person> set = new HashSet<>();
Person p1 = new Person("Lilei", 25);
Person p2 = new Person("Lilei", 25);
set.add(p1);
set.add(p2);
System.out.println("set size is: " + set.size());
//System.out.println("p1 equals p2: " + (p1 == p2));
//System.out.println("set contains p1: " + set.contains(p1));
//System.out.println("set contains p2: " + set.contains(p2));
}
}
set size is: 2
@Override
public int hashCode(){
return name.hashCode() * 31 + age;
}
set size is: 2
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(o instanceof Person){
Person p = (Person)o;
return this.age == p.getAge() && this.name.equals(p.getName());
}
return false;
}
@Override
public int hashCode(){
return name.hashCode() * 31 + age;
}
set size is: 1
If a custom object is used as a key in a Map, then hashCode() and equals() must be overridden
In fact, this point is similar to the second point; there is no need for further examples here.
In fact, the operations of Set objects and Map keys are related to the hashCode and equals methods, such as checking whether this object is in the Set, first locating a segment based on hashCode, and then determining whether it exists using equals, thus avoiding the need to traverse all objects in the Set, which would be inefficient. The same reasoning applies to Map keys.
Thus, the design principles of hashCode and equals are self-evident.
Design Principles of equals()
-
Symmetry: If x.equals(y) returns true, then y.equals(x) should also return true. -
Reflexivity: x.equals(x) must return true. -
Transitivity: If x.equals(y) returns true and y.equals(z) returns true, then z.equals(x) should also return true. -
Consistency: If x.equals(y) returns true, as long as the contents of x and y remain unchanged, no matter how many times you repeat x.equals(y), it should always return true. -
Non-nullity: x.equals(null) should always return false; x.equals(an object of a different type than x) should always return false.
Design Principles of hashCode()
-
During the execution of a Java application, if an object provides the information for equals comparisons without being modified, calling hashCode() on that object multiple times must always return the same integer. -
If two objects are equal according to the equals(Object) method, calling the hashCode() method on both must produce the same integer result. -
It is not required that two objects that are not equal according to the equals(java.lang.Object) method must produce different integer results when calling their respective hashCode() methods. However, programmers should be aware that producing different integer results for different objects can improve the performance of hash tables.
Recommended Reading:
[198] Interviewer: Can you explain the principles of method overloading and method overriding?
[197] Huawei OD Technical Interview Records, providing a reference for future candidates!
[196] Solidifying Fundamentals, Detailed Tutorial on Java 8’s New Features Stream
Scan the QR code to follow my public account
I have read