class Person{ private String name; private int age; public static void sop(Object o){ System.out.println(o); } public Person(String name, int age){ this.name =name; this.age =age; } public void setName(String name){ this.name =name; } public String getName(){ return this.name; } public void setAge(int age){ this.age =age; } public int getAge(){ return this.age; } public String toString(){ return this.name+"::"+this.age; } //equals已經重寫 public boolean equals(Object obj){ if(!(obj instanceof Person)){ return false; } Person p =(Person)obj; //用來查看equals方法是否被調用 sop(this.name +".......equals......."+ p.name); //認爲名字相同而且年齡同樣大的兩個對象是一個 return this.name.equals(p.name) && this.age == p.age; } }
(2). 將hashCode()重寫成相同的值-----解決HashSet中重複添加java
[1]. 問題:內容相同可是地址不一樣的自定義對象如何避免重複的內容添加到HashSet中?this
【解決辦法】必須重寫hashCode和equals這兩個方法spa
{1}. 此時根據底層哈希表的存儲方式:哈希表會將具備相同哈希值的元素依次順延。code
{2}. hashCode值相同,HashSet在存儲對象的時候,equals方法就會起做用orm
{3}. 示例代碼:爲Person類重寫以下的hashCode代碼對象
public int hashCode(){ 內存
System.out.println(this.name +"...hashCode"); 字符串
return 60; get
} hash
這樣,每個Person對象都具備相同的哈希值。
打印結果:
【HashSet添加過程分析】
【1】a1率先存入HashSet中
添加的時候,調用一次a1的hashCode方法,打印一次a1...hashCode。因爲開始HashSet中沒有內容,因此沒有調用a1的equals方法。
內存圖以下:
【2】當a2要存入HashSet的時候,HashSet首先調用a2的hashCode方法,此時打印出a2...hashCode。發現a2的hashCode也是0x3c和a1的地址值都是同樣的,此時要和a1進行內容比較,打印出a2.......equals.......a1。可是發現name和age都不同,因此equals返回false。此時HashSet就將這個a2存到集合中來。
內存圖以下:
【3】當a3要存入HashSet的時候,HashSet首先調用a3的hashCode方法,查看有沒有地址相同的元素,此時打印出a3...hashCode。此時集合中已經存在兩個元素a1和a2,發現a3的hashCode也是0x3c和a一、a2的地址值都是同樣的,此時要一一和這些對象的內容進行比較。
當a3和a2的進行比較時,a3的equals方法被調用,打印出a3.......equals.......a2。 比較發現a3和a2是地址相同可是內容不一樣的元素。
a3再和a1進行比較,a3的equals方法再次被調用,打印出a3.......equals.......a1。
比較發現a3和a1仍然是地址相同可是內容不一樣的元素。
a3就被認爲是集合中之前並不存在的元素,因此仍然被添加進來。
內存圖以下:
【4】當運行到第二個a2要添加進來的時候,先調用hashCode,因此立刻打印a2...hashCode。
可是發現,和a2 hashCode相同的元素有a一、a2和a3。這個a2要和集合中的a一、a2和a3都作內容上是否相同的比較。
a2先比較集合中的a3,調用a2的equals方法,打印出a2.......equals.......a3。
位置相同,可是內容不一樣。
a2再比較集合中的a2,調用a2的equals方法,打印出a2.......equals.......a2。
可是發現二者內容、地址均相同,是重複的元素,不能加到集合中來,因此沒有必要再把這個a2和集合中的a1進行比較。因此沒有打印a2.......equals.......a1
(3). 正確重寫hashCode的辦法
[1]. hashCode重寫成相同的值的缺點
將全部對象的hashCode都返回同樣的值是不科學的。好比a1和a3這兩個根本不一樣的對象,就沒有必要去比較equals,增長無謂的計算量。因此應該對象自己的內容 (屬性)來重寫hashCode。
一旦兩個對象內部不同,就直接斷定出hashCode不同,不用再調用equals進行比較。
[2]. 正確書寫hashCode的辦法:
【原則】按照equals( )中比較兩個對象是否一致的條件用到的屬性來重寫hashCode()。
{1}. 經常使用的辦法就是利用涉及到的的屬性進行線性組合。
{2}. 線性組合過程當中涉及到的組合係數自定義便可。
注意,拼接以後的數值不能超過整形的表達範圍。
{3}. 公式:屬性1的int形式+ C1*屬性2的int形式+ C2*屬性3的int形式+ …
【技巧】當屬性是引用類型的時候,若是已經重寫過hashCode(),那麼這個引用屬性的int形式就是直接調用屬性已有的hashCode值。
最典型的就是這個屬性是字符串類型的,String類型已經重寫了hashCode()方法,因此直接拿來使用便可。
e.g. 分析案例
這個例子中,重寫的equals方法中是經過name和age來斷定兩個對象是否一致的。因此,就經過Person的這兩個屬性name和age的線性組合來獲取這個Person的hashCode值。注意到name是String類型的,因此,能夠調用name的hashCode()來直接獲取name對應的int值。
這裏重寫的hashCode方法是:
public int hashCode(){
sop(this.name +"......hashCode");
return this.name.hashCode() + 29*age;
}
打印結果: