以前一直不是很理解爲何要重寫HashCode和Equals方法,才只能做爲鍵值存儲在HashMap中。經過下文,能夠一探究竟。java
首先,若是咱們直接用如下的Person類做爲鍵,存入HashMap中,會發生髮生什麼狀況呢?segmentfault
public class Person {ide
private String id;this
public Person(String id) {spa
this.id = id;對象
}字符串
}get
import java.util.HashMap;hash
public class Main {class
public static void main(String[] args) {
HashMap<Person, String> map = new HashMap<Person, String>();
map.put(new Person("001"), "findingsea");
map.put(new Person("002"), "linyin");
map.put(new Person("003"), "henrylin");
map.put(new Person("003"), "findingsealy");
System.out.println(map.toString());
System.out.println(map.get(new Person("001")));
System.out.println(map.get(new Person("002")));
System.out.println(map.get(new Person("003")));
}
}
那麼輸出結果是什麼呢?
{Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin}
null
null
null
咱們能夠看到,這裏出現了兩個問題:
那麼,正確的方法其實在不少地方都是被描述過了,直接對Person類進行修改,重載equals和hashCode方法,修改事後的Person類以下:
public class Person {
private String id;
public Person(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (id != null ? !id.equals(person.id) : person.id != null) return false;
return true;
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
那麼,當咱們從新執行上述的檢驗程序時,獲得的結果以下:
{Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy}
findingsea
linyin
findingsealy
能夠看到,以前指出的亮點錯誤都獲得了改正。那麼,爲何會這樣呢?
在HashMap中,查找key的比較順序爲:
顯然,第一步就是要用到hashCode()方法,而第二步就是要用到equals()方法。在沒有進行重載時,在這兩步會默認調用Object類的這兩個方法,而在Object中,Hash Code的計算方法是根據對象的地址進行計算的,那兩個Person("003")的對象地址是不一樣的,因此它們的Hash Code也不一樣,天然HashMap也不會把它們當成是同一個key了。同時,在Object默認的equals()中,也是根據對象的地址進行比較,天然一個Person("003")和另外一個Person("003")是不相等的。
理解了這一點,就很容易搞清楚爲何須要同時重載hashCode()和equals兩個方法了。
還有一個細節,在Person類中對於hashCode()的重在方法爲:
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
這裏可能有疑惑的點在於:爲何能夠用String類型的變量的Hash Code做爲Person類的Hash Code值呢?這樣new Person(new String("003"))和new Person(new String("003"))的Hash Code是相等的嗎?
來看看如下代碼的輸出:
System.out.println("findingsea".hashCode());
System.out.println("findingsea".hashCode());
System.out.println(new String("findingsea").hashCode());
System.out.println(new String("findingsea").hashCode());
728795174
728795174
728795174
728795174
能夠看到四條語句的輸出都是相等的,很直觀的合理的猜想就是String類型也重載了hashCode()以根據字符串的內容來返回Hash Code值,因此相同內容的字符串具備相同的Hash Code。
同時,這也說明了一個問題:爲何在已知hashCode()相等的狀況下,還須要用equals()進行比較呢?就是由於避免出現上述例子中的出現的狀況,由於根據對Person類的hashCode()方法的重載實現,Person類會直接用id這個String類型成員的Hash Code值做爲本身的Hash Code值,可是很顯然的,一個Person("003")和一個String("003")是不相等的,因此在hashCode()相等的狀況下,還須要用equals()進行比較。
如下例子能夠做爲上述說明的佐證:
System.out.println(new Person("003").hashCode()); // 47667
System.out.println(new String("003").hashCode()); // 47667
System.out.println(new Person("003").equals(new String("003"))); // false
以上便是所有。
參考:https://segmentfault.com/a/1190000002655085