1 equals方法
Object類中默認的實現方式是 : return this == obj 。那就是說,只有this 和 obj引用同一個對象,才會返回true。
而我們往往需要用equals來判斷 2個對象是否等價,而非驗證他們的唯一性。這樣我們在實現自己的類時,就要重寫equals.
按照約定,equals要滿足以下規則。
- 自反性: x.equals(x) 一定是true
- 對null: x.equals(null) 一定是false
- 對稱性: x.equals(y) 和 y.equals(x)結果一致
- 傳遞性: a 和 b equals , b 和 c equals,那么 a 和 c也一定equals。
- 一致性: 在某個運行時期間,2個對象的狀態的改變不會影響equals的決策結果,那么,在這個運行時期間,無論調用多少次equals,都返回相同的結果。
舉例:重寫equals方法
1 class Test 2 { 3 private int num; 4 private String data; 5 6 public boolean equals(Object obj) 7 { 8 if (this == obj) 9 return true; 10 11 if ((obj == null) || (obj.getClass() != this.getClass())) 12 return false; 13 14 //能執行到這里,說明obj和this同類且非null。 15 Test test = (Test) obj; 16 return num == test.num&& (data == test.data || (data != null && data.equals(test.data))); 17 } 18 19 public int hashCode() 20 { 21 //重寫equals,也必須重寫hashCode。具體后面介紹。 22 } 23 24 }
2 hashCode方法
這個方法返回對象的散列碼,返回值是int類型的散列碼。
對象的散列碼是為了更好的支持基於哈希機制的Java集合類,例如 Hashtable, HashMap, HashSet 等。
關於hashCode方法,一致的約定是:
- 在某個運行時期間,只要對象的(字段的)變化不會影響equals方法的決策結果,那么,在這個期間,無論調用多少次hashCode,都必須返回同一個散列碼。
- 如果2個對象通過equals調用后返回是true,那么這個2個對象的has
- hCode方法也必須返回同樣的int型散列碼如果2個對象通過equals返回false,他們的hashCode返回的值允許相同。(然而,程序員必須意識到,hashCode返回獨一無二的散列碼,會讓存儲這個對象的hashtables更好地工作。)
重寫了euqls方法的對象必須同時重寫hashCode()方法。
3 為什么必須重寫hashCode方法?
在上面的例子中,Test類對象有2個字段,num和data,這2個字段代表了對象的狀態,他們也用在equals方法中作為評判的依據。那么, 在hashCode方法中,這2個字段也要參與hash值的運算,作為hash運算的中間參數。這點很關鍵,這是為了遵守:2個對象equals,那么 hashCode一定相同規則。
也是說,參與equals函數的字段,也必須都參與hashCode 的計算。
4 重寫hashCode時注意事項
重寫hashCode方法時除了上述一致性約定,還有以下幾點需要注意:
(1)返回的hash值是int型的,防止溢出。
(2)不同的對象返回的hash值應該盡量不同。(為了hashMap等集合的效率問題)
(3)《Java編程思想》中提到一種情況
“設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該產生同樣的值。如果在講一個對象用put()添加進HashMap時產生一個hashCdoe值,而用get()取出時卻產生了另一個hashCode值,那么就無法獲取該對象了。所以如果你的hashCode方法依賴於對象中易變的數據,用戶就要當心了,因為此數據發生變化時,hashCode()方法就會生成一個不同的散列碼”。
舉個例子
public class Test { private int num; private String data; public Test(int num,String data){ this.num = num; this.data = data; } public void setNum(int num) { this.num = num; } public boolean equals(Object obj) { if (this == obj) return true; if ((obj == null) || (obj.getClass() != this.getClass())) return false; Test test = (Test) obj; return num == test.num&& (data == test.data || (data != null && data.equals(test.data))); } public int hashCode() { int hash = 7; hash = 31*hash+num; hash = 31*hash+data.hashCode(); return hash; } public static void main(String[] args) { Map<Test,Integer> map = new HashMap<>(); Test t1 = new Test(21,"ouym"); map.put(t1, 1); t1.setNum(20); System.out.println(map.get(t1)); } }
輸出值為null,我的天吶,hashMap取不到值了。
不重寫equals和hashCode方法的話是不依賴於對象屬性的變化的,也就是說這里使用默認的hashCode方法可以取到值。但是我們重寫equal方法的初衷是判定name和num屬性都相等的Test對象是相等的,而不是說同一個對象的引用才相等,而num=21和num=20明顯不想等,所以這里hashCode返回值不同並不違背設計的初衷。注意上面代碼的使用陷阱。
來自:https://www.cnblogs.com/lulipro/p/5628750.html
