1. 原生equals()方法的内容:
public boolean equals(Object obj) {
return (this == obj);
}
equals方法在其内部是調用了"==",是以說在不重寫equals方法的情況下,equals方法是比較兩個對象是否具有相同的引用,即是否指向了同一個記憶體位址。
而在業務系統中,有時候需要的并不是一種嚴格意義上的相等,而是業務上的對象相等。比如:如果兩個對象中的id相等,那麼就認為這兩個對象是相等的。
2.重寫equals()方法,需要遵守的通用約定
1>自反性
對于任意非null的引用值x,x.equals(x)必須傳回true。
2>對稱性
對于任意非null的引用值x、y,當且僅當y.equals(x)傳回true時,x.equals(y)必須傳回true。
3>一緻性
對于任意非null的引用值x、y,隻要equals方法的比較操作在對象中所用的資訊沒有發生改變,那麼多次調用x.equals(y)應該 一緻的傳回true或false。
4>傳遞性
對于任意非null的引用值x、y、z,如果x.equals(y)傳回true,并且y.equals(z)傳回true,那麼x.equals(z)必須傳回true。
對于任意非null的引用值x,x.equals(null)必須傳回false。
5> 非空性
對于任意null的引用值x,x.equals(null) 一定是false。
3.hashCode()
原生寫法:
public native int hashCode();
hashCode是一個本地方法,他傳回的是這個對象的記憶體位址。
這個方法傳回對象的散列碼,傳回值是int類型的散列碼。
對象的散列碼是為了更好的支援基于哈希機制的Java集合類,例如 Hashtable, HashMap, HashSet 等。
重寫hashCode方法時除了上述一緻性約定,還有以下幾點需要注意:
(1)傳回的hash值是int型的,防止溢出。
(2)不同的對象傳回的hash值應該盡量不同。(為了hashMap等集合的效率問題)
(3)《Java程式設計思想》中提到一種情況
“設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該産生同樣的值。如果在講一個對象用put()添加進HashMap時産生一個hashCdoe值,而用get()取出時卻産生了另一個hashCode值,那麼就無法擷取該對象了。是以如果你的hashCode方法依賴于對象中易變的資料,使用者就要當心了,因為此資料發生變化時,hashCode()方法就會生成一個不同的散列碼”。
4. 重寫equals()方法必須重寫hashCode()方法
原因:hashCode()方法在Object規範中的通用約定第二條,相等的對象必須具有相等的散列碼。
1>在應用運作期間,隻要對象的equals方法的比較操作所用到的資訊沒有被修改,那麼多次調用該對象的equals方法應該始終如一的傳回同一個整數。在同一個應用程式的多次執行過程中,每次執行equals方法所傳回的整數可以不一緻。
2>如果兩個對象使用equals(Object)方法比較是相等的,那麼調用這兩個對象中的任意一個對象的hashCode方法都必須産生相同的一個整數結果。
3>如果兩個對象使用equals(Object)方法比較是不相等的,那麼調用這兩個對象中的任意一個對象的hashCode方法,則不一定要産生不同的整數結果。如果給不同的對象産生不同的hash碼,有可能提高散清單性能(比如往HashMap中添加資料時,具體添加到哪個桶中,就是根據(table.length - 1) & hash來計算的)。
如果一個類重寫了equals方法但是沒有重寫hashCode方法,那麼該類無法結合所有基于散列的集合(HashMap,HashSet)一起正常運作。
5.寫法
31*N可以被編譯器優化為左移5位後減1即31*N =(N<<5)-1,有較高的性能。使用31的原因可能是為了更好的配置設定hash位址,并且31隻占用5bits! 是以從效率上 它是2的5次減1,對計算機來說2的乘除操作隻需要做位移操作,例如*32就是左移5位。 也就是說31對計算機的角度來說運算更快、切占記憶體不多不少,而且形成慣例,虛拟機甚至都專門對他做了優化。是以常用31做系數算hashcode