「==」、「equals()」、「hashcode()」之間的祕密

前言

萬丈高樓平地起,今天的聊點基礎而又常常讓人忽視的話題,好比「==」與「equals()」區別?爲什麼當咱們重寫完"equals()"後也要有必要去重寫"hashcode()"呢? ... 帶着這些問題,咱們一塊兒來探究一下。java

概念

"==":它主要是判斷符號兩邊的「對象」的值是否相等,而這裏的「值「」又有所區分了。面試

基礎數據類型:比較的就是自身的值,這個跟咱們常規的理解是基本一致的。算法

引用數據類型:比較的對象的內存地址。數組

「equals()」:它也是用來判斷兩個對象是否相等,因此也得分不一樣的狀況來講明。微信

在當前類中,沒有重寫equals方法的話,默認的實現跟"=="的實現是同樣的。下面是Object類的equals方法實現。ide

在當前類中,重寫了equals方法,此時判斷的依據就是你重寫的邏輯。函數

怎樣重寫equals()方法?

  • 一、自反性:對於任何非空引用x,x.equals(x)應該返回true。
  • 二、對稱性:對於任何引用x和y,若是x.equals(y)返回true,那麼y.equals(x)也應該返回true。
  • 三、傳遞性:對於任何引用x、y和z,若是x.equals(y)返回true,y.equals(z)返回true,那麼x.equals(z)也應該返回true。
  • 四、一致性:若是x和y引用的對象沒有發生變化,那麼反覆調用x.equals(y)應該返回一樣的結果。
  • 五、非空性:對於任意非空引用x,x.equals(null)應該返回false。

由此能夠看出,重寫一個equals()方法,須要注意的點仍是比較多的,這裏給出一個參考的事例。this

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()方法後,它就必定會出現,也會「吵着「本身也要被重寫。spa

什麼是hashcode()?

hashCode() 的做用是獲取哈希碼,也稱爲散列碼;它返回的一個int整數。這個哈希碼的做用是肯定該對象在哈希表中的索引位置。hashCode方法的主要做用是爲了配合基於散列的集合一塊兒正常運行,這樣的散列集合包括HashSet、HashMap、HashTable等。它定義在JDK的Object.java中,這就意味着Java中的任何類都包含有hashCode() 函數。.net

當咱們在上面的集合插入對象的時候,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全面解讀! 》,連接:https://my.oschina.net/199212...

通過概念的介紹,知道爲何重寫完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不必定相等。因此在一些集合的使用當中會出現問題。

總結

小小的幾個方法,沒想到卻有這麼多「坑」,並且在面試中也會常常被問到,在金三銀四的時候,希望各位不會陷在這裏。

喜歡的話,關注一下微信公衆號《深夜裏的程序猿》,天天更新高質量IT文章

相關文章
相關標籤/搜索