hashCode 與 equals
hashCode和equals方法的關係 面試官可能會問你:“你重寫過 hashcode 和 equals 麼,為什麼重寫equals時必須重寫 hashCode方法?”
首先, equals() 方法和 hashcode() 方法間的關係是這樣的:
1、如果兩個物件相同(即:用 equals 比較返回true),那麼它們的 hashCode 值一定要相同;
2、如果兩個物件的 hashCode 相同,它們並不一定相同(即:用 equals 比較返回 false);
《Effective java》一書中這樣說到:在每個覆蓋了 equals() 方法的類中,也必須覆蓋 hashCode() 方法,如果不這樣做的話,就會違反 Object.hashCode 的通用的約定,從而導致該類無法結合所有基於雜湊的集合一起正常運作,這樣的集合包括HashMap,HashSet 和 HashTable。
先解釋下,為什麼一定要使用 hashcode() 方法:
歸根結底就是為了提高程式的效率才實現了 hashcode() 方法。
程式先進行 hashcode 的比較,如果不同,那沒就不必在進行 equals 的比較了,這樣就大大減少了 equals 比較的次數,這對比需要比較的數量很大的效率提高是很明顯的,一個很好的例子就是在集合中的使用:
- 我們都知道java中的List集合是有序的,因此是可以重複的,而set集合是無序的,因此是不能重複的。那麼怎麼能保證不能被放入重複的元素呢?單靠 equals() 方法比較的話,如果原來集合中有10000個元素,那麼放入第10001個元素時,難道要將前面的所有元素都進行比較,看看是否有重複?這個效率可想而知。
- 因此 hashcode 就應遇而生了,java 就採用了 hash 表,利用雜湊演算法(也叫雜湊演算法),就是將物件資料根據該物件的特徵使用特定的演算法將其定義到一個地址上,那麼在後面定義進來的資料只要看對應的 hashcode 地址上是否有值,那麼就用equals 比較,如果沒有則直接插入,這樣就大大減少了 equals 的使用次數,執行效率就大大提高了。
繼續上面的話題,重寫 hashcode() 方法的原因,簡單的說就是:為了保證是同一個物件,在 equals 比較相同的情況下 hashcode值必定相同。
我們舉例說明,自定義一個Student 類:
1. 只重寫 equals() 方法,不重寫hashcode() 方法:
1 public class Student { 2 private String name; 3 private int age; 4 5 public Student(String name, int age) { 6 super(); 7 this.name = name; 8 this.age = age; 9 } 10 @Override 11 public boolean equals(Object obj) { 12 if (this == obj) 13 return true; 14 if (obj == null) 15 return false; 16 if (getClass() != obj.getClass()) 17 return false; 18 Student other = (Student) obj; 19 if (age != other.age) 20 return false; 21 if (name == null) { 22 if (other.name != null) 23 return false; 24 } else if (!name.equals(other.name)) 25 return false; 26 return true; 27 } 28 29 public String getName() { 30 return name; 31 } 32 33 public void setName(String name) { 34 this.name = name; 35 } 36 37 public int getAge() { 38 return age; 39 } 40 41 public void setAge(int age) { 42 this.age = age; 43 } 44 }
執行一下測試類:
1 public class hashTest { 2 public static void test() { 3 Student stu1 = new Student("Gabit",21); 4 Student stu2 = new Student("Gabit",21); 5 6 System.out.println("兩位同學是同一個人嗎?"+stu1.equals(stu2)); 7 System.out.println("stu1.hashCode() = "+stu1.hashCode()); 8 System.out.println("stu1.hashCode() = "+stu2.hashCode()); 9 } 10 11 public static void main(String[] args) { 12 test(); 13 } 14 15 }
檢視執行結果:
1 兩位同學是同一個人嗎?true 2 stu1.hashCode() = 1163157884 3 stu1.hashCode() = 1956725890
如果重寫了 equals() 而未重寫 hashcode() 方法,可能就會出現兩個沒有關係的物件 equals 相同(因為equal都是根據物件的特徵進行重寫的),但 hashcode 不相同的情況。因為此時 Student 類的 hashcode 方法就是 Object 預設的 hashcode方 法,由於預設的 hashcode 方法是根據物件的記憶體地址經雜湊演算法得來的,所以 stu1 != stu2,故兩者的 hashcode 值不一定相等。
根據 hashcode 的規則,兩個物件相等其 hash 值一定要相等,矛盾就這樣產生了。上面我們已經解釋了為什麼要使用 hashcode 演算法,所以即使字面量相等,但是產生兩個不同的 hashCode 值顯然不是我們想要的結果。
2. 如果我們在重寫 equals() 時,也重寫了 hashCode() 方法:
1 public class Student { 2 private String name; 3 private int age; 4 5 public Student(String name, int age) { 6 super(); 7 this.name = name; 8 this.age = age; 9 } 10 11 @Override 12 public int hashCode() { 13 final int prime = 31; 14 int result = 1; 15 result = prime * result + age; 16 result = prime * result + ((name == null) ? 0 : name.hashCode()); 17 return result; 18 } 19 20 @Override 21 public boolean equals(Object obj) { 22 if (this == obj) 23 return true; 24 if (obj == null) 25 return false; 26 if (getClass() != obj.getClass()) 27 return false; 28 Student other = (Student) obj; 29 if (age != other.age) 30 return false; 31 if (name == null) { 32 if (other.name != null) 33 return false; 34 } else if (!name.equals(other.name)) 35 return false; 36 return true; 37 } 38 39 public String getName() { 40 return name; 41 } 42 43 public void setName(String name) { 44 this.name = name; 45 } 46 47 public int getAge() { 48 return age; 49 } 50 51 public void setAge(int age) { 52 this.age = age; 53 } 54 }
執行結果:
1 兩位同學是同一個人嗎?true 2 stu1.hashCode() = 71578563 3 stu1.hashCode() = 71578563
從 Student 類重寫後的 hashcode() 方法中可以看出,重寫後返回的新的 hash 值與 Student 的兩個屬性是有關,這樣就確保了物件和物件地址之間的關聯性。
一句話:實現了“兩個物件 equals 相等,那麼地址也一定相同”的概念!
最後附上大佬的原文連結:https://blog.csdn.net/weixin_44259720/article/details/88414828