前言
萬丈高樓平地起,今天的聊點基礎而又經常讓人忽視的話題,比如“==”與“equals()”區別?為何當我們重寫完"equals()"後也要有必要去重寫"hashcode()"呢? ... 帶著這些問題,我們一起來探究一下。
概念
"==":它主要是判斷符號兩邊的“物件”的值是否相等,而這裡的“值“”又有所區分了。
基礎資料型別:比較的就是自身的值,這個跟我們常規的理解是基本一致的。
引用資料型別:比較的物件的記憶體地址。
“equals()”:它也是用來判斷兩個物件是否相等,所以也得分不同的情況來說明。
在當前類中,沒有重寫equals方法的話,預設的實現跟"=="的實現是一樣的。下面是Object類的equals方法實現。
在當前類中,重寫了equals方法,此時判斷的依據就是你重寫的邏輯。
怎樣重寫equals()方法?
- 1、自反性:對於任何非空引用x,x.equals(x)應該返回true。
- 2、對稱性:對於任何引用x和y,如果x.equals(y)返回true,那麼y.equals(x)也應該返回true。
- 3、傳遞性:對於任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那麼x.equals(z)也應該返回true。
- 4、一致性:如果x和y引用的物件沒有發生變化,那麼反覆呼叫x.equals(y)應該返回同樣的結果。
- 5、非空性:對於任意非空引用x,x.equals(null)應該返回false。
由此可以看出,重寫一個equals()方法,需要注意的點還是比較多的,這裡給出一個參考的事例。
public class EqualsDemo {
private String name;
private String info;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EqualsDemo that = (EqualsDemo) o;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return info != null ? info.equals(that.info) : that.info == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (info != null ? info.hashCode() : 0);
return result;
}
}
複製程式碼
有些讀者可能會感到奇怪,不是說重寫equals()方法嗎,為什麼這裡又出現了一個hashcode()?所以這裡又引出了我們的另一個主角hashcode()方法,當我們重寫了equals()方法後,它就一定會出現,也會“吵著“自己也要被重寫。
什麼是hashcode()?
hashCode() 的作用是獲取雜湊碼,也稱為雜湊碼;它返回的一個int整數。這個雜湊碼的作用是確定該物件在雜湊表中的索引位置。hashCode方法的主要作用是為了配合基於雜湊的集合一起正常執行,這樣的雜湊集合包括HashSet、HashMap、HashTable等。它定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode() 函式。
當我們在上面的集合插入物件的時候,java是怎麼知道里面是否有重複的物件呢?可能大家第一反應是equals方法,沒錯這方法可以實現這個功能,但是當集合裡面有成千上萬個元素的時候,效率會如何呢?答案當然是比較差了,所以才會出現了雜湊碼。
public V put(K key, V value) {
//判斷當前陣列是否等於{},若是則初始化陣列
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
//判斷 key 是否等於 null,是則將把當前鍵值對新增進table[0]中,遍歷table[0]連結串列
//如果已經有null為key的Entry,則修改值,返回舊值,若無則直接新增。
if (key == null)
return putForNullKey(value);
//key不為null則計算hash
int hash = hash(key);
//搜尋對應hash所在的table中的索引
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//修改次數
modCount++;
addEntry(hash, key, value, i);
return null;
}
複製程式碼
這裡是jdk7中 Hashmap put()方法的實現,通過原始碼的註釋可以看出執行的流程,需要更詳細的瞭解HashMap可以參考我之前發在開源中國的部落格《Java7 HashMap全面解讀! 》,連結:my.oschina.net/19921228/bl…
經過概念的介紹,知道為什麼重寫完equals()後要接著重寫hashcode()了吧?
People p1=new People("小明",18);
People p2=new People("小明",18);
複製程式碼
此時重寫了equals方法,p1.equals(p2)一定返回true,假如只重寫equals而不重寫hashcode,那麼Student類的hashcode方法就是Object預設的hashcode方法,由於預設的hashcode方法是根據物件的記憶體地址經雜湊演算法得來的,顯然此時s1!=s2,故兩者的hashcode不一定相等。所以在一些集合的使用當中會出現問題。
總結
小小的幾個方法,沒想到卻有這麼多“坑”,而且在面試中也會經常被問到,在金三銀四的時候,但願各位不會陷在這裡。