1. 程式人生 > >聽說你還搞不定java中的==和equals?

聽說你還搞不定java中的==和equals?

相信很多讀者關於==equals懂了又懵,懵了又懂,如此迴圈,事實上可能是因為看到的部落格文章之類的太多了,長篇大論,加上一段時間的洗禮之後就迷路了。本篇文章再一次理清楚。當然如果覺得本文太囉嗦的話,當然我也考慮到了,因為我也不喜歡長篇大論囉裡囉嗦比比叨叨胡攪蠻纏的文章,畢竟大家入門java 的時候就知道個大概了,因此記住一句話就好了:equals本身和 == 沒有區別,對於基本資料都是比較值,對於引用型別,則比較的是所指向的物件的地址!其他類在繼承Object類之後對equals方法重寫,所以表現的是比較裡面的內容!具體比較的則要看自己是怎麼重寫的。

好了,如果有興趣的就看下文,當然不感興趣的大佬可以點個贊直接走了,不用看了,會了還看個*啊,樓主你個憨憨(皮一下很開心)

1、原生的equals()方法本身與 “ == ”沒有任何區別!

從java語言本質上來講,"=="屬於JAVA語言的運算子,而equals則是根類Object的一個方法。

關於Object類的equals()方法,我們可以看看其原始碼

  /*
     * @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is the same as the obj
     *          argument; {@code false} otherwise.
     * @see     #hashCode()
     * @see     java.util.HashMap
     */
public boolean equals(Object obj) {
    return (this == obj);
}

是的,equals底層其實就是“ == ”,也就是說,原生的equals()方法本身與 “ == ”沒有任何區別!唯一的區別則是基本型別沒有繼承Object類,所以基本型別沒有equals()方法,也就是說基本型別只能使用“ == ”判斷值是否相等。

既然原生的equals()方法本身與 “ == ”沒有任何區別,那麼我們對運算子 “ == ”的使用有所瞭解即可!

運算子 “ == ”其具體作用是用來比較值是否相等,這裡分兩中情況:

  • 1、基本資料型別的變數,則直接比較其儲存的 “值”是否相等;

  • 2、引用型別的變數,則比較的是所指向的物件的地址是否相等;

到這裡我們可以初步確認原生的equals()方法本身與 “ == ”沒有任何區別!作用正是如上。

2、equals()方法的重寫

但是重點來了,因為對於equals()方法我一直在強調原生二字。是的,讓很多初學者疑惑的點就在這裡:equals()方法的重寫!

在JDK中,諸如StringDate等類對equals方法進行了重寫,以String為例,這裡感興趣的讀者可以一起看看String類中重寫的equals()方法,當然跳過也問題不大

/**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        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;
    }

從原始碼中可以看出,首先該方法判斷比較的是所指向的物件地址是否相等,如果相同直接返回true,如果不相同進行下一個if判斷,第二個if判斷大致意思則是比較其儲存的 “值”是否相等,也就是比較內容值!相同就返回true,比如兩個new String物件“AAA”“AAA”,這裡雖然物件地址不相等,但是內容相等,所以同樣返回true。

這裡我就想給各位出個典型的String例子了:

public static void main(String[] args) {
       String a = "宜春";
       String b = new String("宜春");
       String c = b; //注意這裡是引用傳遞,意思是c也指向b指向的記憶體地址
       
       System.out.println(a == b);  //false
       System.out.println(a == c);  //false
       System.out.println(b == c);  //true
       System.out.println(a.equals(b));  //true
       System.out.println(a.equals(c));  //true
       System.out.println(b.equals(c));  //true
    }

【特別注意:String型別屬於引用型別】

解析:
(1)a == b?意思是地址指向的是同一塊地方嗎?很明顯不一樣。

(2)a == c?意思是地址指向的是同一塊地方嗎?很明顯不一樣。

(3)b == c?意思是地址指向的是同一塊地方嗎?很明顯內容一樣,所以為true。

(4)a.equals( b )?意思是地址指向的內容一樣嘛?一樣。

(4)a.equals( c )?意思是地址指向的內容一樣嘛?一樣。

(4)b.equals( c )?意思是地址指向的內容一樣嘛?一樣。

當然,你可能還是有點疑惑,那麼結合下面這張圖再理解上面的解析,你可能就恍然大悟了

OK。現在能理解嘛?你你你......不用回答,我知道你理解了(理直氣壯)。當然值得注意一點的是String中intern()方法,先看一段程式:

    public static void main(String[] args) {
       String a = "宜春";
       String b = new String("宜春");
       b=b.intern();

       System.out.println(a == b);  //true
       System.out.println(a.equals(b));  //true
    }

intern方法的意思是檢查字串池裡是否存在,如果存在了那就直接返回為true。因此在這裡首先a會在字串池裡面有一個,然後 b.intern()一看池子裡有了,就不再新建new了,直接把b指向它。

3、為什麼要重寫equals方法?

不知道大家有沒有想過這個問題。當然答案也是很簡單的,因為程式設計師比較字串一般比較其內容就好了,比較記憶體地址是不是同一個物件就好像沒啥意義了,重寫equals方法就很方便用來比較字串的內容了。

其實除了諸如String、Date等類對equals方法進行重寫,我們在實際開發中,我們也常常會根據自己的業務需求重寫equals方法.

舉個栗子:
我們的需求就是如果兩個學生物件姓名、身份證號、性別相等,我們認為兩個學生物件相等,不一定需要學生物件地址相同。

學生A的個人資訊(姓名:如花,性別:女,身份證號:123,住址:廣州),學生A物件地址為0x11,
學生B的個人資訊(姓名:如花,性別:女,身份證號:123,住址:深圳),學生A物件地址為0x12,

這時候如果不重寫Object的equals方法,那麼返回的一定是false不相等,這個時候就需要我們根據自己的需求重寫equals()方法了。具體equals方法的重寫程式碼如下:

// 重寫equals方法
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Student)) {
       // instanceof 已經處理了obj = null的情況
			return false;
		}
		Student stuObj = (Student) obj;
		// 物件地址相等
		if (this == stuObj) {
			return true;
		}
		// 如果兩個物件姓名、身份證號碼、性別相等,我們認為兩個物件相等
		if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.IDnumber.equals(this.IDnumber)) {
			return true;
		} else {
			return false;
		}
	}

開發中這樣設計,才能符合我們的生活!到這裡我就不信了你還搞不定==和equals!

可是一涉及重寫equals方法的同時又衍生了下面一個問題。

4、重寫equals方法之後要不要重寫hashCode()方法?

當然這個問題要討論,又要長篇大論嗶嗶一大堆了,有空寫一篇這樣的文章吧專門討論討論這個問題,當然園子裡的大佬們也寫了一大堆!可以自行去了解了解。本篇文章簡單聊聊,點到即可。

首先hashCode()方法是Object類的一個方法,原始碼如下:

/**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java<font size="-2"><sup>TM</sup></font> programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();

可以看出hashCode()方法返回的就是一個int數值,從方法的名稱上就可以看出,其目的是生成一個hash碼。hash碼的主要用途就是在對物件進行雜湊的時候作為key輸入,據此很容易推斷出,我們需要每個物件的hash碼儘可能不同,這樣才能保證雜湊的存取效能。

事實上,Object類提供的預設實現確實保證每個物件的hash碼不同(在物件的記憶體地址基礎上經過特定演算法返回一個hash碼)。Java採用了雜湊表的原理。雜湊演算法也稱為雜湊演算法,是將資料依特定演算法直接指定到一個地址上。初學者可以這樣理解,hashCode方法實際上返回的就是物件儲存的實體地址(實際上不是)。

想要知道hashCode的作用,必須要先知道Java中的集合。

Java中的集合List的元素是有序的,元素可以重複;Set元素無序,但元素不可重複。這我們都清楚。但是你有沒有想過這樣一個問題:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?

每錯這裡就是用Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後新增到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要呼叫1000次equals方法。這顯然會大大降低效率。

那怎麼解決呢?我們可以在Java集合框架中得到驗證。由於HashSet是基於HashMap來實現的,所以這裡只看HashMapput方法即可。原始碼如下:

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        //這裡通過雜湊值定位到物件的大概儲存位置
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //if語句中,先比較hashcode,再呼叫equals()比較
            //由於“&&”具有短路的功能,只要hashcode不同,也無需再呼叫equals方法
            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;
}

正如原始碼中註釋所述,“&&”具有短路的功能,只要hashcode不同,也無需再呼叫equals方法。是的,Java採用了雜湊表的原理。 雜湊表具有優越的查詢效能,就像九九乘法表2*3=6你能很輕易知道,但是雜湊表難免還會出現雜湊衝突,只是概率極低。

如此設計,這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。
如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就呼叫它的equals方法與新元素進行比較,相同的話就不存,不相同就雜湊其它的地址。所以這裡存在一個衝突解決的問題。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。

因此並不是重寫了equals方法就一定要重寫hashCode方法,只有用到HashMap,HashSet等Java集合的時候重寫了equals方法就一定要重寫hashCode方法。用不到雜湊表僅僅重寫equals()方法也OK的。

Java官方建議 重寫equals()就一定要重寫hashCode()方法。畢竟實際開發場景中常常用到Java集合

5、eqauls方法和hashCode方法關係

Java對於eqauls方法和hashCode方法是這樣規定的:

1、如果兩個物件equals為true ,他們的hashcode一定相等。
2、如果兩個物件equals為false,他們的hashcode有可能相等。
3、如果兩個物件hashcode相等,equals不一定為true。
4、如果兩個物件hashcode不相等,equals一定為false。

最後,若有不足或者不正之處,歡迎指正批評,感激不盡!

歡迎各位關注我的公眾號,裡面有一些java學習資料和一大波java電子書籍,比如說周志明老師的深入java虛擬機器、java程式設計思想、核心技術卷、大話設計模式、java併發程式設計實戰.....都是java的聖經,不說了快上Tomcat車,咋們走!最主要的是一起探討技術,嚮往技術,追求技術,說好了來了就是盆友喔...

相關推薦

聽說不定java的==equals

相信很多讀者關於==和equals懂了又懵,懵了又懂,如此迴圈,事實上可能是因為看到的部落格文章之類的太多了,長篇大論,加上一段時間的洗禮之後就迷路了。本篇文章再一次理清楚。當然如果覺得本文太囉嗦的話,當然我也考慮到了,因為我也不喜歡長篇大論囉裡囉嗦比比叨叨胡攪蠻纏的文章,畢竟大家入門java 的時候就知道個

真的明白Java基本資料型別的轉換了嗎?

寫這篇部落格源於在進行長連線通訊的時候我們需要將流資料和我們的String、基本型別的資料之間進行轉換,我們知道byte[]與String之間的轉換相當方便,那麼接下來我們就要弄懂byte[]與基本資料型別之間的轉換了。 計算機中的儲存 首先

Java“==”equals()”的區別

spa logs bsp 指向 monday class code equals out “==”比較的是變量所指向的對象,當S1在內存中定義以後,再定義s2的時候s2所指向的值是定義s1時候所創建的,而不是又在內存創建了一個“Monda

java “==” equals 的區別

通過 引用 而在 program 值範圍 兩個 比較 copy mon   在初學Java時,可能會經常碰到下面的代碼: 1 String str1 = new String("hello"); 2 String str2 = new String("hello");

Java==equals區別詳解+案例

兩個 布爾型 整數 返回 boolean 和equal clas 定義 true 一開始遇見==和equals我也是分不清,後來看了很多博客,收益匪淺, 擔心以後給忘了,所以寫下這個,以後復習可以用。 (有哪裏寫得不對的,希望可以留言幫忙改進,大家一起共同進步) 一、Jav

java==equalshashcode的區別詳解

style void ted 基本類型 內存 class 存儲 throw rgs 一、相同點 都是用來進行值或對象的比較。 二、不同點 對於“==”而言,對於基本類型(char,byte,short,int,long,float,double

java==equals

tr1 ring author string類型 print nbsp als 方法 date /** * @author zhaojiatao * @date 2018/7/19 */ public class equalsLearn { public

java==equals的區別詳解

字符 blog 的區別 代碼塊 數組 sss oss 兩種 內存劃分 分析前基礎了解: 一)JVM把內存劃分成兩種:一種是棧內存,一種是堆內存。   ①在函數中定義的一些基本類型的變量和對象的引用變量(變量名)都在函數的棧內存中分配。   ②當在一段代碼塊定義一個變量時

Java基礎------java==equals的區別

前言 (1)==介紹 1、如果作用於基本資料型別的變數,則直接比較其儲存的“值”是否相等。 2、如果作用於引用型別的變數,則比較的是所指向的物件地址。 (2)equals()方法 1、equals方法不能作用於基本資料型別的變數。 2、如果沒有對eq

Java==equals(),equalsIgnoreCase()

關於==和equals,我們需要知道java中的資料型別,可分為兩類: 1.基本資料型別,也稱原始資料型別。byte,short,char,int,long,float,double,boolean  他們之間的比較,應用雙等號(==),比較的是他們的值。  2.複合資料

Java "==" equals 的區別

話說,關於”==” equals的比較網上已經有很多大神總結了,但我在查詢博文時,仍感覺有些支離破碎,有不能一篇概全的不爽,而大神們都有寫的很味道的部分,所以本文主要是引用別人好的文章作出”最全”總結。 當然,我的總結肯定會在我的認知的基礎上進行的,所以此文只

java==equals的區別

我們分別解釋兩者區別 ==:        java的變數型別分為值型別和引用型別,如果比較的是值型別那麼直接比較的是值是否相等,        如果比較的是引用型別那麼比較的是記憶體地址。 equals:        equals是Object類的一個方法,如果

java面試(1): java==equalshashCode的區別

1."=="     "=="運算子是比較兩個變數的值是否相等。也就是說,該運算子用於比較變數對應的記憶體中所儲存的值是否相等,要比較兩個基礎型別的資料或兩個引用變數是否相等,只能使用"=="運算子。     具體而言,如果兩個變數是基礎

Java==equals的區別,equalshashCode的區別

在java中: ==是運算子,用於比較兩個變數是否相等。 equals,是Objec類的方法,用於比較兩個物件是否相等,預設Object類的equals方法是比較兩個物件的地址,跟==的結果一樣。Ob

java==equalshashCode的區別

1.概念 ==:操作符,比較兩個物件之間的數值關係,返回boolean型別 equals:Object類的方法,比較兩個物件內容關係,返回boolean型別 hashCode:Object類的方法,返回物件的hash值 2.具體分析 2.1 ==

Java==equals的區別(詳細)

要想充分了解==和equals的區別需要對java的記憶體有所瞭解: Java把記憶體劃分成兩種:一種是棧記憶體,一種是堆記憶體。  在函式中定義的一些基本型別的變數和物件的引用變數(變數名)都在函式的棧記憶體中分配。  當在一段程式碼塊定義一個變數時,Java就在棧中為

Java == equals的區別以及equals的重寫

==和 equals 都是比較的,而前者是運算子,後者則是一個方法基本資料型別和引用資料型別都可以使用運算子==,  而只有引用型別資料才可以使用 equals==操作符專門用來比較兩個變數的值是否相等,也就是用於比較變數所對應的記憶體中所儲存的數值是否相同要比較兩個基本型別

Javaequals()hashCode()

sea 接口 後來 ide itl 一個數 毫無 exceptio title 概述 在我們使用類集框架(比方使用hashMap、hashSet)的時候,常常會涉及到重寫equals()和hashCode()這兩個方法。 這兩個方法的聯系是:

真的會用java的三目運算符嗎

tmp nbu mar bce dft class eth 黃金 fzu 我也慨嘆不已,想我當初15級裝備王者黃金弓時攻擊力才達到180,現在要是到了15級再裝備的話,攻擊力就遠遠不是那麽低了! 一陣動亂之後,大約十幾個水晶螃蟹的仇恨立刻全部被吸引了過去,我立刻眼疾手快的換

javaequals==的區別

 1)對於==,如果作用於基本資料型別的變數,則直接比較其儲存的 “值”是否相等;     如果作用於引用型別的變數,則比較的是所指向的物件的地址   2)對於equals方法,注意:equals方法不能作用於基本資料型別的變數     如果沒有對equals方法進行重寫,則比較的是引用