日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java 中的 ==, equals 與 hashCode 的區(qū)別與聯(lián)系

 liang1234_ 2019-01-27

一、概述

1、概念

  • == : 該操作符生成的是一個boolean結(jié)果,它計算的是操作數(shù)的值之間的關(guān)系
  • equals : Object 的 實例方法,比較兩個對象的content是否相同
  • hashCode : Object 的 native方法 , 獲取對象的哈希值,用于確定該對象在哈希表中的索引位置,它實際上是一個int型整數(shù)

二、關(guān)系操作符 ==

1、操作數(shù)的值

  • 基本數(shù)據(jù)類型變量

    在Java中有八種基本數(shù)據(jù)類型:

      浮點型:float(4 byte), double(8 byte)

      整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte)

      字符型: char(2 byte)

      布爾型: boolean(JVM規(guī)范沒有明確規(guī)定其所占的空間大小,僅規(guī)定其只能夠取字面值”true”和”false”)

      對于這八種基本數(shù)據(jù)類型的變量,變量直接存儲的是“值”。因此,在使用關(guān)系操作符 == 來進行比較時,比較的就是“值”本身。要注意的是,浮點型和整型都是有符號類型的(最高位僅用于表示正負,不參與計算【以 byte 為例,其范圍為 -2^7 ~ 2^7 - 1,-0即-128】),而char是無符號類型的(所有位均參與計算,所以char類型取值范圍為0~2^16-1)。


  • 引用類型變量
    在Java中,引用類型的變量存儲的并不是“值”本身,而是與其關(guān)聯(lián)的對象在內(nèi)存中的地址。比如下面這行代碼,
    String str1;
  • 1

  這句話聲明了一個引用類型的變量,此時它并沒有和任何對象關(guān)聯(lián)。
  而通過 new 來產(chǎn)生一個對象,并將這個對象和str1進行綁定:

str1= new String("hello");
  • 1

  那么 str1 就指向了這個對象,此時引用變量str1中存儲的是它指向的對象在內(nèi)存中的存儲地址,并不是“值”本身,也就是說并不是直接存儲的字符串”hello”。這里面的引用和 C/C++ 中的指針很類似。


2、小結(jié)

 因此,對于關(guān)系操作符 ==:

  • 若操作數(shù)的類型是基本數(shù)據(jù)類型,則該關(guān)系操作符判斷的是左右兩邊操作數(shù)的是否相等
  • 若操作數(shù)的類型是引用數(shù)據(jù)類型,則該關(guān)系操作符判斷的是左右兩邊操作數(shù)的內(nèi)存地址是否相同。也就是說,若此時返回true,則該操作符作用的一定是同一個對象。

三、equals方法

1、來源
  equals方法是基類Object中的實例方法,因此對所有繼承于Object的類都會有該方法。
  
  在 Object 中的聲明:

    public boolean equals(Object obj) {}
  • 1

2、equals方法的作用
 初衷 : 判斷兩個對象的 content 是否相同

 為了更直觀地理解equals方法的作用,我們先看Object類中equals方法的實現(xiàn)。

  public boolean equals(Object obj) {
    return (this == obj);
  }
  • 1
  • 2
  • 3

  很顯然,在Object類中,equals方法是用來比較兩個對象的引用是否相等,即是否指向同一個對象。

  但我們都知道,下面代碼輸出為 true:

public class Main {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");

        System.out.println(str1.equals(str2));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

原來是 String 類重寫了 equals 方法:

public boolean equals(Object anObject) {   // 方法簽名與 Object類 中的一致
    if (this == anObject) {     // 先判斷引用是否相同(是否為同一對象),
        return true;
    }
    if (anObject instanceof String) {   // 再判斷類型是否一致,
        // 最后判斷內(nèi)容是否一致.
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

即對于諸如“字符串比較時用的什么方法,內(nèi)部實現(xiàn)如何?”之類問題的回答即為:

使用equals方法,內(nèi)部實現(xiàn)分為三個步驟:

  • 比較引用是否相同(是否為同一對象),
  • 判斷類型是否一致(是否為同一類型),
  • 最后 比較內(nèi)容是否一致

Java 中所有內(nèi)置的類的 equals 方法的實現(xiàn)步驟均是如此,特別是諸如 Integer,Double 等包裝器類。


3、equals 重寫原則

對象內(nèi)容的比較才是設(shè)計equals()的真正目的,Java語言對equals()的要求如下,這些要求是重寫該方法時必須遵循的:

  • 對稱性: 如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true” ;

  • 自反性: x.equals(x)必須返回是“true” ;

  • 類推性: 如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也應(yīng)該返回是“true” ;

  • 一致性: 如果x.equals(y)返回是“true”,只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是“true” ;

  • 對稱性: 如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true”。

  • 任何情況下,x.equals(null)【應(yīng)使用關(guān)系比較符 ==】,永遠返回是“false”;x.equals(和x不同類型的對象)永遠返回是“false”


4、小結(jié)
 因此,對于 equals 方法:

  • 本意比較兩個對象的 content 是否相同
  • 必要的時候,我們需要重寫該方法,避免違背本意,且要遵循上述原則

四、hashCode 方法

1、hashCode 的來源
  hashCode 方法是基類Object中的 實例native方法,因此對所有繼承于Object的類都會有該方法。
  
  在 Object類 中的聲明(native方法暗示這些方法是有實現(xiàn)體的,但并不提供實現(xiàn)體,因為其實現(xiàn)體是由非java語言在外面實現(xiàn)的):

     public native int hashCode();
  • 1

2、哈希相關(guān)概念
 我們首先來了解一下哈希表:

  • 概念 : Hash 就是把任意長度的輸入(又叫做預(yù)映射, pre-image),通過散列算法,變換成固定長度的輸出(int),該輸出就是散列值。這種轉(zhuǎn)換是一種 壓縮映射,也就是說,散列值的空間通常遠小于輸入的空間。不同的輸入可能會散列成相同的輸出,從而不可能從散列值來唯一的確定輸入值。簡單的說,就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)。

  • 應(yīng)用–數(shù)據(jù)結(jié)構(gòu) : 數(shù)組的特點是:尋址容易,插入和刪除困難; 而鏈表的特點是:尋址困難,插入和刪除容易。那么我們能不能綜合兩者的特性,做出一種尋址容易,插入和刪除也容易的數(shù)據(jù)結(jié)構(gòu)?答案是肯定的,這就是我們要提起的哈希表,哈希表有多種不同的實現(xiàn)方法,我接下來解釋的是最常用的一種方法——拉鏈法,我們可以理解為 “鏈表的數(shù)組”,如圖:

             這里寫圖片描述
                            圖1 哈希表示例

     左邊很明顯是個數(shù)組,數(shù)組的每個成員是一個鏈表。該數(shù)據(jù)結(jié)構(gòu)所容納的所有元素均包含一個指針,用于元素間的鏈接。我們根據(jù)元素的自身特征把元素分配到不同的鏈表中去,也是根據(jù)這些特征,找到正確的鏈表,再從鏈表中找出這個元素。其中,將根據(jù)元素特征計算元素數(shù)組下標(biāo)的方法就是散列法。

  • 拉鏈法的適用范圍 : 快速查找,刪除的基本數(shù)據(jù)結(jié)構(gòu),通常需要總數(shù)據(jù)量可以放入內(nèi)存。

  • 要點 :
    hash函數(shù)選擇,針對字符串,整數(shù),排列,具體相應(yīng)的hash方法;
    碰撞處理,一種是open hashing,也稱為拉鏈法,另一種就是closed hashing,也稱開地址法,opened addressing。

3、hashCode 簡述
 在 Java 中,由 Object 類定義的 hashCode 方法會針對不同的對象返回不同的整數(shù)。(這是通過將該對象的內(nèi)部地址轉(zhuǎn)換成一個整數(shù)來實現(xiàn)的,但是 JavaTM 編程語言不需要這種實現(xiàn)技巧)。

 hashCode 的常規(guī)協(xié)定是:

  • 在 Java 應(yīng)用程序執(zhí)行期間,在對同一對象多次調(diào)用 hashCode 方法時,必須一致地返回相同的整數(shù),前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行,該整數(shù)無需保持一致。

  • 如果根據(jù) equals(Object) 方法,兩個對象是相等的,那么對這兩個對象中的每個對象調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果。

  • 如果根據(jù) equals(java.lang.Object) 方法,兩個對象不相等,那么對這兩個對象中的任一對象上調(diào)用 hashCode 方法 不要求 一定生成不同的整數(shù)結(jié)果。但是,程序員應(yīng)該意識到,為不相等的對象生成不同整數(shù)結(jié)果可以提高哈希表的性能。
     
      要想進一步了解 hashCode 的作用,我們必須先要了解Java中的容器,因為 HashCode 只是在需要用到哈希算法的數(shù)據(jù)結(jié)構(gòu)中才有用,比如 HashSet, HashMap 和 Hashtable。

      Java中的集合(Collection)有三類,一類是List,一類是Queue,再有一類就是Set。 前兩個集合內(nèi)的元素是有序的,元素可以重復(fù);最后一個集合內(nèi)的元素?zé)o序,但元素不可重復(fù)。

      那么, 這里就有一個比較嚴重的問題:要想保證元素不重復(fù),可兩個元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢? 這就是 Object.equals 方法了。但是,如果每增加一個元素就檢查一次,那么當(dāng)元素很多時,后添加到集合中的元素比較的次數(shù)就非常多了。 也就是說,如果集合中現(xiàn)在已經(jīng)有1000個元素,那么第1001個元素加入集合時,它就要調(diào)用1000次equals方法。這顯然會大大降低效率。于是,Java采用了哈希表的原理。 這樣,我們對每個要存入集合的元素使用哈希算法算出一個值,然后根據(jù)該值計算出元素應(yīng)該在數(shù)組的位置。所以,當(dāng)集合要添加新的元素時,可分為兩個步驟:
         

  • 先調(diào)用這個元素的 hashCode 方法,然后根據(jù)所得到的值計算出元素應(yīng)該在數(shù)組的位置。如果這個位置上沒有元素,那么直接將它存儲在這個位置上;

  • 如果這個位置上已經(jīng)有元素了,那么調(diào)用它的equals方法與新元素進行比較:相同的話就不存了,否則,將其存在這個位置對應(yīng)的鏈表中(Java 中 HashSet, HashMap 和 Hashtable的實現(xiàn)總將元素放到鏈表的表頭)。


4、equals 與 hashCode

 前提: 談到hashCode就不得不說equals方法,二者均是Object類里的方法。由于Object類是所有類的基類,所以一切類里都可以重寫這兩個方法。

  • 原則 1 : 如果 x.equals(y) 返回 “true”,那么 x 和 y 的 hashCode() 必須相等 ;
  • 原則 2 : 如果 x.equals(y) 返回 “false”,那么 x 和 y 的 hashCode() 有可能相等,也有可能不等 ;
  • 原則 3 : 如果 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 一定返回 “false” ;
  • 原則 4 : 一般來講,equals 這個方法是給用戶調(diào)用的,而 hashcode 方法一般用戶不會去調(diào)用 ;
  • 原則 5 : 當(dāng)一個對象類型作為集合對象的元素時,那么這個對象應(yīng)該擁有自己的equals()和hashCode()設(shè)計,而且要遵守前面所說的幾個原則。

5、實現(xiàn)例證

 hashCode()在object類中定義如下:

public native int hashCode();
  • 1

 說明是一個本地方法,它的實現(xiàn)是根據(jù)本地機器相關(guān)的。

 String 類是這樣重寫它的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];     //成員變量1

    /** The offset is the first index of the storage that is used. */
    private final int offset;      //成員變量2

    /** The count is the number of characters in the String. */
    private final int count;       //成員變量3

   /** Cache the hash code for the string */
    private int hash; // Default to 0    //非成員變量

    public int hashCode() {
    int h = hash;
        int len = count;         //用到成員變量3
    if (h == 0 && len > 0) {
        int off = offset;         //用到成員變量2
        char val[] = value;       //用到成員變量1
            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];       //遞推公式
            }
            hash = h;
        }
        return h;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

對程序的解釋:h = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],由此可以看出,對象的hash地址不一定是實際的內(nèi)存地址。


五、小結(jié)

  • hashcode是系統(tǒng)用來快速檢索對象而使用
  • equals方法本意是用來判斷引用的對象是否一致
  • 重寫equals方法和hashcode方法時,equals方法中用到的成員變量也必定會在hashcode方法中用到,只不過前者作為比較項,后者作為生成摘要的信息項,本質(zhì)上所用到的數(shù)據(jù)是一樣的,從而保證二者的一致性

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多