正確重寫hashCode的辦法
http://blog.sina.com.cn/s/blog_700aa8830101jtlf.html
轉自:http://blog.csdn.net/benjaminzhang666/article/details/9468605
——————————————————————————————————————————
正確重寫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()方法,所以直接拿來使用即可。
——————————————————————————————————————————
(1). 主要代碼
- 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中重復添加
[1]. 問題:內容相同但是地址不同的自定義對象如何避免重復的內容添加到HashSet中?
【解決辦法】必須重寫hashCode和equals這兩個方法
{1}. 此時根據底層哈希表的存儲方式:哈希表會將具有相同哈希值的元素依次順延。
{2}. hashCode值相同,HashSet在存儲對象的時候,equals方法就會起作用
{3}. 示例代碼:為Person類重寫如下的hashCode代碼
- public int hashCode(){
- System.out.println(this.name +"...hashCode");
- return 60;
- }
這樣,每一個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和a1、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相同的元素有a1、a2和a3。這個a2要和集合中的a1、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;
}
打印結果: