equals()和==、hashcode()

一、equals()和==

1.以下是Object類中equals()的源碼

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

2.以下是Integer類中equals()的源碼

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            //返回當(dāng)前對(duì)象的值與參數(shù)對(duì)象的值的比較
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

3.以下是String類中equals()的源碼

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        //判斷參數(shù)是否為String類對(duì)象,不是直接返回false
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            //比較每個(gè)字符
            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;
    }

由以上源碼我們可以得出:
① equal和==最根本的區(qū)別在于equal是一個(gè)方法,而==是一個(gè)運(yùn)算符。
② 基本數(shù)據(jù)類型只能用==來進(jìn)行比較。在object類中equal()方法就是用==實(shí)現(xiàn)的,但是對(duì)于,Math、Integer、Double等這些封裝類在使用equals()方法時(shí),已經(jīng)覆蓋了object類的equals()方法,他們比較的是內(nèi)容(值)。
③ 在String類中equals()也被重寫了,因此字符串比較返回true有兩種情況(==:地址內(nèi)容都一致,equals():內(nèi)容一致)

題外話:下面代碼的運(yùn)行結(jié)果是什么?解釋一下為什么會(huì)有這些差異。

String s1 = "hello";
String s2 = s1 + ",world";
String s3 = "hello" + ",world";
String s4 = "hello,world";
String s5 = new String("hello,world");

System.out.println(s2.equals(s4)); // true
System.out.println(s2==s4);     // false
System.out.println(s3==s4);     // true
System.out.println(s4==s5);     // false

一般情況下,創(chuàng)建字符串對(duì)象有兩種方式,一種是字面值創(chuàng)建,一種是通過 new 得方式創(chuàng)建,這兩者是不一樣的。

如果是字面值創(chuàng)建的方式,如 String s4="hello,world",JVM會(huì)先去字符串常量池中尋找有沒有“hello,world”這個(gè)字符串,若有,則將其地址給 s4;若沒有,則先在常量池里創(chuàng)建“hello,world”,然后再把地址給s4;而通過 new 的方式創(chuàng)建對(duì)象,則是在堆中創(chuàng)建“hello,world”對(duì)象,s5 指向這個(gè)對(duì)象。還是看圖吧,比較直觀:

字符串的拼接
String 類被 final 修飾,因此字符串不能修改,當(dāng)兩個(gè)字符串相加時(shí),是先生成 StringBuilder 對(duì)象,然后通過 append() 的方式將兩個(gè)字符串拼接,再調(diào)用 toString() 方法生成新的字符串對(duì)象。所以只考慮 s1 和 s2,他們?cè)?JVM 中是這樣的:

但是像 String s3 = "hello" + ",world" 這樣直接兩個(gè)字面值相加的,java文件在編譯期間就已經(jīng)將這條語句做了優(yōu)化,將其直接變成 "hello,world",等到運(yùn)行的時(shí)候就查找字符串常量池,因此 s3 == s4 返回的結(jié)果就為 true。

String、StringBuilder、StringBuffer
String 是常量,且每次需要在原來字符串基礎(chǔ)上擴(kuò)展都需要新建對(duì)象,導(dǎo)致速度會(huì)稍微慢一點(diǎn),而且占內(nèi)存,因此對(duì)于需要經(jīng)常擴(kuò)展的字符串,可以使用 StringBuilder 和 StringBuffer。但是 StringBuilder 和 StringBuffer 又有區(qū)別,即使兩者很像,在速度上 StringBuilder 會(huì)快一些,因?yàn)?StringBuffer 的操作加了 synchronized ,即加了鎖,使得操作相對(duì)比較慢,就舉 append() 為例子,StringBuffer 中此方法的源碼如下:

所以,一般在單線程的情況下,可以選擇使用 StringBuilder;在多線程的情況下可以選擇 StringBuffer .

總結(jié)

回到一開始的程序,我們可以大致地畫出在 JVM 中的存儲(chǔ): 

二、hashcode()

hashCode()方法給對(duì)象返回一個(gè)hash code值。

性質(zhì):

  • 在一個(gè)Java應(yīng)用的執(zhí)行期間,如果一個(gè)對(duì)象提供給equals做比較的信息沒有被修改的話,該對(duì)象多次調(diào)用hashCode()方法,該方法必須始終如一返回同一個(gè)integer。
  • 如果兩個(gè)對(duì)象根據(jù)equals(Object)方法是相等的,那么調(diào)用二者各自的hashCode()方法必須產(chǎn)生同一個(gè)integer結(jié)果。
  • 并不要求根據(jù)equals(java.lang.Object)方法不相等的兩個(gè)對(duì)象,調(diào)用二者各自的hashCode()方法必須產(chǎn)生不同的integer結(jié)果。[1]


1.以下是Object類中hashcode()的源碼,說明是一個(gè)本地方法,它的實(shí)現(xiàn)是根據(jù)本地機(jī)器相關(guān)的。大量的實(shí)踐表明,由Object類定義的hashCode()方法對(duì)于不同的對(duì)象返回不同的integer

public native int hashCode();

2.以下是String類中hashcode()的源碼,可以看出字符串的內(nèi)容相同hashcode也相同。

public int hashCode() {
        int h = hash;   //hash默認(rèn)值是0
        //字符串的內(nèi)容相同hashcode也相同
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

3.以下是AbstractSet類中hashcode()和equals()的源碼
在set集合中,jvm就是根據(jù)hashcode(),和equals()這兩個(gè)方法來判斷元素是否重復(fù),如果要重寫,建議兩個(gè)方法一起重寫,否則可能會(huì)出現(xiàn)安全性問題。

public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }
 public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }

4.以下是AbstractList類中hashcode()和equals()的源碼

 public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

參考:
https://mp.weixin.qq.com/s/2rTxX3Co-2bDR16wZE-WwQ
https://www.cnblogs.com/Qian123/p/5703507.html


  1. 兩個(gè)對(duì)象equals()結(jié)果相同則兩者h(yuǎn)ashcode()也相同;而兩者h(yuǎn)ashcode()相同,equals()結(jié)果不一定相同;因此,在Set中判斷元素是否重復(fù)的其中一個(gè)重要原則就是先判斷hashcode,若相同再判斷equals()是否為true。 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容