面試中equals和==還傻傻的分不清少办?

內(nèi)容目錄

[TOC]

面試復(fù)現(xiàn)

Hello,大家好诵原,我是你們的老朋友大黃英妓。可能很多同學(xué)在面試過(guò)程中對(duì)于并發(fā)的知識(shí)绍赛、Spring等核心知識(shí)準(zhǔn)備的比較充分蔓纠,而且回答的比較好。但是當(dāng)剛好是面試的時(shí)候吗蚌,大部分面試官面試的難度都是由易變難的腿倚,剛開(kāi)始面試,面試官準(zhǔn)備用一道簡(jiǎn)單的題目來(lái)熱熱身子褪测,暖暖場(chǎng)猴誊。相反這些問(wèn)題才是整場(chǎng)面試的關(guān)鍵,回答的不好侮措,面試官失去了繼續(xù)聊下去的欲望了懈叹。

下面為常見(jiàn)的面試對(duì)話

面試官:

大黃同學(xué)是吧?你來(lái)說(shuō)一下equals和==之間的區(qū)別吧分扎?通常在哪些地方會(huì)用上呢澄成?

大黃:
此刻內(nèi)心波動(dòng),好家伙這題我會(huì),我可是早早準(zhǔn)備過(guò)的墨状。

== 對(duì)于不同的類型比較的是不一樣的卫漫,基礎(chǔ)類型對(duì)比的是值是否相同,引用類型對(duì)比的是引用是否相同肾砂;而 equals 則是比較的值是否相同

面試官:

你確定是這樣的嗎列赎?對(duì)于所有的對(duì)象equals都是比較的值嗎?

大黃:
為啥這么問(wèn)镐确,這題我準(zhǔn)備過(guò)的包吝,就是這樣的吧。源葫。

我印象中確實(shí)是這樣的诗越,equals 比較的確實(shí)值是否相同。

面試官此刻內(nèi)心mmp息堂,這也不清楚嚷狞,趕快結(jié)束,我還得回去工作呢荣堰。臉上卻還是笑嘻嘻的說(shuō)

嗯床未,好的,你下去再看看持隧。我們?cè)賳?wèn)問(wèn)其他的問(wèn)題

過(guò)兩天之后即硼,面試結(jié)果杳無(wú)音信,好點(diǎn)的公司可能會(huì)發(fā)送一條感謝信屡拨。

【xx出行】感謝您對(duì)xx機(jī)會(huì)的關(guān)注只酥,誠(chéng)邀您參與我們的應(yīng)聘者體驗(yàn)調(diào)研,以幫助我們提升招聘體驗(yàn)呀狼。鏈接:https://page.xiaojukeji.com/active/ddpage_0aGzHCjT.html?callback=TWpjNU16UTBOdz09

痛定思痛的大黃決定從此以后牢牢解決這個(gè)問(wèn)題裂允,翻遍天下奇書(shū)、搜索各大論壇得到如下真經(jīng)哥艇。

大黃小課堂

1. ==解讀

對(duì)于基本類型和引用類型 == 的作用效果是不同的绝编,兩者比較如下

  • 基本類型:比較的是值是否相同;
  • 引用類型:比較的是引用是否相同貌踏;

我們可以來(lái)看一個(gè)簡(jiǎn)單的實(shí)例:

public static void main(String[] args) {
    String x = "Hello";
    String y = "Hello";
    String z = new String("Hello");

    System.out.println(x==y);   // true
    System.out.println(x==z); // false
}

System.out.println(x==y); 對(duì)于引用對(duì)象的x和y十饥,指向的是同一個(gè)字符串,兩個(gè)引用是相同的祖乳,因此輸出true逗堵。

System.out.println(x==z); 對(duì)于字符串z會(huì)指向新new出來(lái)的一個(gè)字符串對(duì)象,x與z指向的不是同一個(gè)字符串對(duì)象眷昆。

具體的內(nèi)存可以參見(jiàn)下圖:

字符串在Jvm中的布局

2. equals含義

equals 本質(zhì)上就是 ==蜒秤,只不過(guò) String 和 Integer 等重寫(xiě)了 equals 方法汁咏,把它變成了值比較∽髅模看下面的代碼就明白了攘滩,如果沒(méi)有重寫(xiě)equals方法,默認(rèn)是按照==進(jìn)行比較纸泡,如果是普通的對(duì)象漂问,則比較的是引用類型。

  • 如果是基本類型弟灼,則采用的是Object的equals方法進(jìn)行比較
  • 如果是字符串级解,String類重寫(xiě)了equals方法冒黑,采用String的equals方法比較
  • 如果是一般對(duì)象田绑,并且沒(méi)有重寫(xiě)equals方法,則默認(rèn)采用的==進(jìn)行比較

先看例子抡爹,在看代碼掩驱。

public class EqualsAndSame {

    public static void main(String[] args) {
        String x = "Hello";
        String y = "Hello";
        Integer a = 1;
        Integer b = 1;
        // 因?yàn)楸容^的是基本類型,采用的基本數(shù)據(jù)類型重寫(xiě)的equals方法冬竟,比較的是值
        System.out.println(a.equals(b));
        // 因?yàn)楸容^的是字符串欧穴,采用的String重寫(xiě)的equals方法,比較的是各個(gè)字符串
        System.out.println(x.equals(y)); // true

        Person lisi0 = new Person("lisi",21);
        Person lisi1 = new Person("lisi",21);
        // 因?yàn)楸容^的是對(duì)象泵殴,因?yàn)镻erson類沒(méi)有重寫(xiě)自己的equals方法涮帘,
        // 因此默認(rèn)采用Object的equals()方法
        System.out.println(lisi0.equals(lisi1));
    }
}


class Person{

    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

1. Object類中的equals()

equals()如果不被重寫(xiě),則底層比較方式和==基本一致笑诅,底層就是通過(guò)==來(lái)比較的调缨。

public boolean equals(Object obj) {
  return (this == obj);
}

2. 基本類型的equals比較

/**
 * 本質(zhì)上比較Integer對(duì)象的值
 * @param obj      比較的對(duì)象
 * @return
 */
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

3. String的equals()方法

/**
 * 比較兩個(gè)字符串是否相等
 * 當(dāng)anObject對(duì)象不為空且該對(duì)象與比較對(duì)象完全相等是返回true
 * @param anObject
 * @return
 */
public boolean equals(Object anObject) {
    // 1. 如果被比較對(duì)象與anObject完全相等時(shí)則返回true
    if (this == anObject) {
        return true;
    }

    // 判斷字符是否是字符串類型
    /**
     * 2.1 首先判斷兩個(gè)字符串長(zhǎng)度是否相等,相等則繼續(xù)比較內(nèi)部元素吆你;否則直接返回false
     * 2.2 利用while循環(huán)依次比較兩個(gè)數(shù)組內(nèi)部字符是否相等
     */
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

修煉到這種功程度弦叶,下次面對(duì)這種面試題目,可以這么說(shuō):

==對(duì)于不同類型比較的是不同的妇多,基礎(chǔ)類型對(duì)比的是值是否相同伤哺,引用類型對(duì)比的是引用是否相同;而 equals 對(duì)于不同的類型比較的是不同的者祖,對(duì)于基礎(chǔ)類型因此基礎(chǔ)類本身實(shí)現(xiàn)了equals()方法立莉,其底層比較的是值是否相同,對(duì)于String比較的是字符串內(nèi)容是否完全相同七问,而對(duì)于其他的類型蜓耻,如果本身沒(méi)有重寫(xiě)equals()方法,其比較的是引用是否相同烂瘫,如果實(shí)現(xiàn)了equals()方法媒熊,則按照實(shí)現(xiàn)方法的邏輯進(jìn)行比較奇适。

當(dāng)然回答到這里可能已經(jīng)很好的回答了面試官的問(wèn)題,但是這題還沒(méi)有完芦鳍,可能面試官還會(huì)繼續(xù)追問(wèn)嚷往。

面試官:

你說(shuō)一下,一般重寫(xiě)了自己的equals()方法柠衅,還需要重寫(xiě)什么方法呢皮仁?

大黃:

還需要額外重寫(xiě)hashCode方法。

面試官:

歐菲宴,為什么必須要重寫(xiě)hashCode()方法贷祈,如果不重寫(xiě)會(huì)有什么問(wèn)題呢?

大黃:

hashCode()主要應(yīng)用于計(jì)算對(duì)象的hashCode值喝峦,通常會(huì)用于一些框架势誊,比如hashMap的key值比較,在hashMap中判斷一個(gè)key和另一個(gè)key是否沖突的時(shí)候谣蠢,就是通過(guò)hashCode來(lái)計(jì)算key的下標(biāo)粟耻。如果重寫(xiě)equals(),而沒(méi)有重寫(xiě)hashCode()方法時(shí)眉踱,因?yàn)槭莾蓚€(gè)都是new的對(duì)象挤忙,所以即使里面的值一樣,但是對(duì)象所處的地址卻不同谈喳,所以使用默認(rèn)的hashCode也就不同册烈,當(dāng)然在hashMap中就不會(huì)認(rèn)為兩個(gè)是一個(gè)對(duì)象。這樣就會(huì)產(chǎn)生set進(jìn)去了一個(gè)對(duì)象婿禽,拿著同樣的key卻取不出來(lái)赏僧。

大黃小課堂第二講

上面說(shuō)到了重寫(xiě)equals()方法必須重寫(xiě)hashCode()方法,只是說(shuō)到了面試中應(yīng)該怎么回答谈宛,如果對(duì)于上面回答不是很理解次哈,下面將深入的講解一番。

1. 源碼一窺

hashCode()方法

本身是一個(gè)本地方法吆录,也就是說(shuō)本身是在本地層面進(jìn)行求解hashCode()的值的窑滞,返回值時(shí)int。

public native int hashCode();

源碼中對(duì)于hashCode()也有一些約束恢筝,源碼備注中的翻譯如下:

  1. 如果對(duì)象在使用equals方法中進(jìn)行比較的參數(shù)沒(méi)有修改哀卫,那么多次調(diào)用一個(gè)對(duì)象的hashCode()方法返回的哈希值應(yīng)該是相同的

  2. 如果兩個(gè)對(duì)象通過(guò)equals方法比較是相等的撬槽,那么要求這兩個(gè)對(duì)象的hashCode方法返回的值也應(yīng)該是相等的此改。

  3. 如果兩個(gè)對(duì)象通過(guò)equals方法比較是不同的,那么也不要求這兩個(gè)對(duì)象的hashCode方法返回的值是不相同的侄柔。(換句話說(shuō)兩個(gè)對(duì)象equals()不相同共啃,這兩個(gè)對(duì)象的hashCode()方法可等可不等)但是我們應(yīng)該知道對(duì)于不同對(duì)象產(chǎn)生不同的哈希值對(duì)于哈希表(HashMap)能夠提高性能占调。

hashCode()的源碼中一直介紹hashMap,那我們可以看看hashMap中對(duì)于hashCode()的應(yīng)用移剪,先看一下HashMap的基本結(jié)構(gòu)(關(guān)于hashMap的知識(shí)網(wǎng)上有很多知識(shí)究珊,此處不再贅述)

hashMap結(jié)構(gòu)

?<p style="text-align:center">(圖片來(lái)自于美團(tuán)技術(shù)團(tuán)隊(duì))</p>

那么是如何確定一個(gè)數(shù)據(jù)存儲(chǔ)在數(shù)組中的哪個(gè)位置呢?就是通過(guò)hashCode方法進(jìn)行計(jì)算出存儲(chǔ)在哪個(gè)位置纵苛,上面hashCode()規(guī)則時(shí)說(shuō)了有可能兩個(gè)不同對(duì)象的hashCode方法返回的值相同剿涮,那么此時(shí)就會(huì)產(chǎn)生沖突,產(chǎn)生沖突的話就會(huì)調(diào)用equals方法進(jìn)行比對(duì)攻人,如果不同取试,那么就將其加入鏈表,如果相同就替換原數(shù)據(jù)怀吻。

本身hashMap容器時(shí)重寫(xiě)了HashCode()方法的瞬浓,但是本質(zhì)上也是調(diào)用Object的hashCode()方法

public final int hashCode() {
  return Objects.hashCode(key) ^ Objects.hashCode(value);
}

如果自定義對(duì)象作為key但是不重寫(xiě)hashCode()方法會(huì)怎么樣呢?

先看一下沒(méi)有重寫(xiě)hashCode()的對(duì)象
Person定義如下:

class Person{

    private String name;
    private Integer age;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

public class EqualsAndSame {

    public static void main(String[] args) {
        Map<Person, Integer> personMap = new HashMap<>();
        Person person1 = new Person("zhangsan",12);
        Person person3 = new Person("zhangsan",12);
        personMap.put(person2, 2222);
        System.out.println(personMap.get(person3));
    }
}

輸出結(jié)果出人意料確實(shí)null烙博,沒(méi)有按照預(yù)獲取到2222瑟蜈,為啥會(huì)這樣呢?

HashMap是采用hashCode()用來(lái)計(jì)算該對(duì)象放入數(shù)組中的哪個(gè)位置渣窜,兩個(gè)都是重寫(xiě)new的對(duì)象,但是由于沒(méi)有重寫(xiě)宪躯,默認(rèn)都是采用ObjecthashCode()乔宿,雖然里面的內(nèi)容都相同,但是對(duì)象所處的地址卻不同访雪,所以使用默認(rèn)的hashCode也就不同详瑞,get()方法就以為是獲取另一個(gè)key值。返回的自然是null

如何解決這個(gè)問(wèn)題呢臣缀?
可以重寫(xiě)equals()方法和hashCode()方法坝橡,可以通過(guò)Idea快捷鍵自動(dòng)生成equals()hashCode()方法

class Person{
    ...
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(age, person.age);
    }

    /**
     * 重寫(xiě)的hashCode()方法是通過(guò)對(duì)象的全部字段來(lái)計(jì)算hashCode值
     * 最底層是通過(guò)Arrays的hashCode()來(lái)計(jì)算
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Objects.hash()底層是通過(guò)Arrays.hashCode()實(shí)現(xiàn)的

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;
    int result = 1;
    // 遍歷對(duì)象的每個(gè)字段,通過(guò)字段的hashCode()方法累加得到
    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

重寫(xiě)了之后精置,兩個(gè)對(duì)象只要各個(gè)字段的值相同计寇,對(duì)象的hashCode值必定相同,這也就是為什么重寫(xiě)equals()方法的時(shí)候要求必須重寫(xiě)hashCode()方法了

好了脂倦,今天的大黃每日成長(zhǎng)技能到此結(jié)束了番宁。感謝各位大佬的觀看,下篇再見(jiàn)赖阻。

平時(shí)不積累蝶押,面試空流淚。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末火欧,一起剝皮案震驚了整個(gè)濱河市棋电,隨后出現(xiàn)的幾起案子茎截,更是在濱河造成了極大的恐慌,老刑警劉巖赶盔,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稼虎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡招刨,警方通過(guò)查閱死者的電腦和手機(jī)霎俩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沉眶,“玉大人打却,你說(shuō)我怎么就攤上這事』丫螅” “怎么了柳击?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)片习。 經(jīng)常有香客問(wèn)我捌肴,道長(zhǎng),這世上最難降的妖魔是什么藕咏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任状知,我火速辦了婚禮,結(jié)果婚禮上孽查,老公的妹妹穿的比我還像新娘饥悴。我一直安慰自己,他們只是感情好盲再,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布西设。 她就那樣靜靜地躺著,像睡著了一般答朋。 火紅的嫁衣襯著肌膚如雪贷揽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天梦碗,我揣著相機(jī)與錄音禽绪,去河邊找鬼。 笑死叉弦,一個(gè)胖子當(dāng)著我的面吹牛丐一,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淹冰,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼库车,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了樱拴?” 一聲冷哼從身側(cè)響起柠衍,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤洋满,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后珍坊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體牺勾,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年阵漏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驻民。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡履怯,死狀恐怖回还,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叹洲,我是刑警寧澤柠硕,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站运提,受9級(jí)特大地震影響蝗柔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜民泵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一癣丧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洪灯,春花似錦坎缭、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坏快。三九已至铅檩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間莽鸿,已是汗流浹背昧旨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祥得,地道東北人兔沃。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像级及,于是被迫代替她去往敵國(guó)和親乒疏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容