如之前提到的對hashCode()的分析:【Java】hashCode特性、算法和作用
1. 如何重寫hashCode()方法
Google首席Java架構師Joshua Bloch在他的著作《Effective Java》中提出了一種簡單通用的hashCode算法
- 初始化一個整形變量,爲此變量賦予一個非零的常數值,比如int result = 17;
- 選取equals方法中用於比較的所有域,然後針對每個域的屬性進行計算:
(1) 如果是boolean值,則計算 f ? 1:0
(2) 如果是byte\char\short\int,則計算**(int)f**
(3) 如果是long值,則計算**(int)(f ^ (f >>> 32))**
(4) 如果是float值,則計算Float.floatToIntBits(f)
(5) 如果是double值,則計算Double.doubleToLongBits(f),然後返回的結果是long,再用規則(3)去處理long,得到int
(6) 如果是對象應用,如果equals方法中採取遞歸調用的比較方式,那麼hashCode中同樣採取遞歸調用hashCode的方式。否則需要爲這個域計算一個範式,比如當這個域的值爲null的時候,那麼hashCode 值爲0
(7) 如果是數組,那麼需要爲每個元素當做單獨的域來處理。如果你使用的是1.5及以上版本的JDK,那麼沒必要自己去重新遍歷一遍數組,java.util.Arrays.hashCode方法包含了8種基本類型數組和引用數組的hashCode計算,算法同上,
java.util.Arrays.hashCode(long[])的具體實現:
public static int hashCode(long a[]) {
if (a == null)
return 0;
int result = 1;
for (long element : a) {
int elementHash = (int)(element ^ (element >>> 32));
result = 31 * result + elementHash;
}
return result;
}
- 對於涉及到的各個字段,採用第二步中的方式,將其依次應用於下式:
result = result * 31 + [hashCode];
補充說明一點:
如果初始值result不取17而取0的話,則對於hashCode爲0的字段來說就沒有區分度了,這樣更容易產生衝突。比如兩個自定義類中,一個類比另一個類多出來一個或者幾個字段,其餘字段全部一樣,分別new出來2個對象,這2個對象共有的字段的值全是一樣的,而對於多來的那些字段的值正好都是0,並且在計算hashCode時這些多出來的字段又是最先計算的,這樣的話,則這兩個對象的hashCode就會產生衝突。還是那句話,hashCode方法的實現沒有最好,只有更好。
2. hashCode() 重寫固定模板
故總結出hashCode()重寫的固定模板如下:
/**
* 重寫hashCode方法
*/
@Override
public int hashCode() {
int result = 17;
// boolean 類型
result = 31 * result + (this.mBoolean == flase ? 0 : 1);
// int 類型
result = 31 * result + this.mInt;
// float 類型
result = 31 * result + Float.floatToIntBits(this.mFloat);
// long 類型
result = 31 * result + (int)(this.mLong ^ (this.mLong >>> 32));
// double 類型
result = 31 * result + Float.valueOf(Double.doubleToLongBits(this.mDouble)).hashCode();
// String 類型
result = 31 * result + (this.mString == null ? 0 : this.mString.hashCode());
// Object 類型
result = 31 * result + (this.mObj == null ? 0 : this.mObj.hashCode());
return result;
}
例子代碼中的類:
class Worker {
private String name;
private int age;
private double salary;
//...略
}
/**
* 重寫hashCode方法
*/
@Override
public int hashCode() {
int result = 17;
// int 類型
result = 31 * result + this.age;
// double 類型
result = 31 * result + Float.valueOf(Double.doubleToLongBits(this.salary)).hashCode();
// String 類型
result = 31 * result + (this.name == null ? 0 : this.name.hashCode());
return result;
}
3. 如何重寫equals()方法
碼來:
/**
* 5.5 爲Worker添加equals方法,轉字符串比較
*/
@Override
public boolean equals(Object obj) {
// 相同判斷
if (this == obj) {
return true;
}
// null判斷
if (obj == null) {
return false;
}
// 類型一致判斷
if (this.getClass() != obj.getClass()) {
return false;
}
// 拆箱操作(類型一致)
Worker w = (Worker)obj;
// 比較內容(比較所有成員的值)
if (this.toString().equals(w.toString())) {
return true;
}
return false;
}
驗證如上圖(本文就1張圖)。
重寫覆蓋父類Object.equals()方法,五步走:
* 1.判斷引用地址是否相同
* 2.判斷引用地址是否爲空
* 3.確認對象類型是否一致
* 4.轉型 - 向下轉型拆箱
* 5.比較對象中的實際內容
hashCode與equals的關係總結:
1.hashcode相等,兩個對象不一定相等,需要通過equals方法進一步判斷;
2.hashcode不相等,兩個對象一定不相等;
3.equals方法爲true,則hashcode肯定一樣;
4.equals方法爲false,則hashcode不一定不一樣。