日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開源 > 綜合技術(shù) > 對(duì)象相等性——如何給自定義對(duì)象添加equals和hashCode方法

對(duì)象相等性——如何給自定義對(duì)象添加equals和hashCode方法

來(lái)源:程序員人生   發(fā)布時(shí)間:2016-07-11 09:08:04 閱讀次數(shù):3071次

譯自 http://www.javaworld.com/article/2072762/java-app-dev/object-equality.html

每一個(gè)Java對(duì)象都從java.lang.Object繼承了1些方法:

Creational methods
Object()Default no-argument constructor
clone()Returns a new instance of the class
**Equality methods**
equals(Object)Returns true if this instance is equals to the argument
hashCode()Returns a hash code based on the instance data
**Synchronizing methods**
notify()Sends a signal to a waiting thread (on the current instance)
notifyAll()Sends a signal to all waiting threads (on the current instance)
wait()Forces the current thread to wait for a signal (on the current instance)
**Other methods**
toString()Returns a string representation of the object
finalize()Perform garbage-collection duties
getClass()Returns the Class object associated with the instance

這些方法都提供了默許實(shí)現(xiàn),其中除notify(), notifyAll(), wait()3個(gè)方法是final的,沒法被子類重寫,其他方法都可以被重寫。這篇文章將討論如何重寫equals()hashCode()方法。

equals()和hashCode()方法做甚么?

equals()方法的目的是判斷參數(shù)對(duì)象和當(dāng)前實(shí)例是不是相等。實(shí)際上,java.util包中的所有集合類都使用了該方法,還有其他很多較為底層的庫(kù)(如RMI,JDBC,等等)都隱式地依賴于該方法的正確性。如果兩個(gè)對(duì)象被認(rèn)為是相等的,那末該方法返回tree,否則返回false。哪些內(nèi)容相等才被認(rèn)為是兩個(gè)對(duì)象相等由每一個(gè)類自己定義。

由于計(jì)算對(duì)象是不是相等是1件很耗時(shí)的事,Java提供了1種快速判斷兩個(gè)對(duì)象是不是相等的方法,即hashCode()。該方法根據(jù)對(duì)象的內(nèi)部數(shù)據(jù)結(jié)構(gòu)生成1個(gè)小的數(shù)值,被稱為哈希碼(hash code),如果兩個(gè)對(duì)象具有不同的哈希碼,那末他們不可能相等。(比如字典里的兩個(gè)英文單詞,如果它們都以A開頭,那末它們有可能相等,如果1個(gè)以A開頭,1個(gè)以B開頭,那末它們不可能相等。)

計(jì)算哈希碼的目的是哈希要比計(jì)算全部對(duì)象的相等性快。HashMap就使用了哈希碼來(lái)盡量地避免計(jì)算對(duì)象的相等性,HashMapList快的1個(gè)緣由就是,List需要搜索全部數(shù)據(jù)結(jié)構(gòu)判斷對(duì)象是不是存在,而HashMap只需搜索那些具有相同哈希值的對(duì)象。

切記,1個(gè)類只重寫equals()方法而不重寫hashCode()方法是毛病的。在繼承體系中,只需父類提供1個(gè)hashCode()方法便可,后面會(huì)詳細(xì)討論。

實(shí)現(xiàn)equals()方法

方法簽名必須為

public boolean equals(Object obj)
`</pre>

注意:任何類的`equals()`方法的參數(shù)都必須是`Object`類型,否則該方法就不是重寫,而是重載了,當(dāng)判斷兩個(gè)對(duì)象是不是相等時(shí)就會(huì)調(diào)用`java.lang.Object`類的默許的`equals()`方法,而非你定義的。

Javadoc中描寫`equals()`方法必須滿足:
  • 自反性(Reflexive),1個(gè)對(duì)象必須和本身相等,即`a.equals(a);
  • 對(duì)稱性(Symmetric),即如果a.equals(b),那末b.equals(a)
  • 傳遞性(Transitive),即如果a.equals(b),并且b.equals(c),那末a.equals(c)
  • 非null,1個(gè)對(duì)象任什么時(shí)候候都不能等于null,即a.equals(null)永久返回false。

    根據(jù)上面規(guī)則,很容易寫出1個(gè)equals()方法的實(shí)現(xiàn),只需要比較以下內(nèi)容:

    1. 如果參數(shù)是this,返回true;(自反性)
    2. 如果參數(shù)是null,返回false;(非null)
    3. 如果參數(shù)類型和當(dāng)前對(duì)象類型不同,返回false;(對(duì)稱性)
    4. 所有非static和非transient域都是相等的。(對(duì)稱性,傳遞性)

    為什么不需要比較static域和transient域?由于static數(shù)據(jù)是屬于類的而非對(duì)象實(shí)例,所有對(duì)象實(shí)例同享static數(shù)據(jù)。transient關(guān)鍵字的目的是對(duì)象在序列化時(shí)不讓某些域?qū)懭耄ㄈ鐬榘踩鹨姡脩舻你y行卡號(hào)和密碼等信息,不希望在網(wǎng)絡(luò)操作中被傳輸,那末對(duì)這些變量加上transient關(guān)鍵字后就不會(huì)被持久化 [詳見]),如果這些域用于測(cè)試對(duì)象是不是相等,那末同1個(gè)對(duì)象在序列化之前和以后就會(huì)不等。

    以2維平面坐標(biāo)中的點(diǎn)Point為例,我們實(shí)現(xiàn)1個(gè)簡(jiǎn)單的equals()方法:

    `public class Point {
        private static double version = 1.0;
        private transient double distance;
        private int x, y;
    
    
    public boolean equals(Object other) {
        if (other == this) return true;
        if (other == null) return false;
        if (getClass() != other.getClass()) return false;
        Point point = (Point)other;
        return (x == point.x &amp;&amp; y = point.y);
    }
    

    }
    `

    注意:這里使用getClass()來(lái)比較兩個(gè)對(duì)象是不是屬于同1個(gè)類型,而非instancof,后面我們會(huì)討論為什么不用instanceof

    比較援用類型

    如果1個(gè)對(duì)象里含有援用類型,那末如何比較兩個(gè)對(duì)象的援用是不是相等?答案是根據(jù)以下規(guī)則:

    1. 如果this的援用變量為null,那末other相應(yīng)的援用變量也必須為null
    2. 如果this的援用變量不為null,那末它必須和other相應(yīng)的援用變量equals()

    以下面的Person類,含有兩個(gè)援用類型的變量name和birth:

    `public class Person {
        private String name;
        private Date birth;
    
    
    public boolean equals(Object other) {
        if (other == this) return true;
        if (other == null) return false;
        if (getClass() != other.getClass()) return false;
        Person person = (Person)other;
        return (name == person.name || (name != null &amp;&amp; name.equals(person.name)))
        &amp;&amp; (birth == person.birth || (birth != null &amp;&amp; birth.equals(person.birth)));
    }
    

    }
    `

    注意:name == person.name檢查了兩個(gè)援用都為null的情況和兩個(gè)援用指向同1個(gè)對(duì)象的情況。在調(diào)用name.equals(person.name)方法前1定要先檢查name != null,否則將會(huì)拋出’NullPointerException’。

    實(shí)現(xiàn)hashCode()方法

    哈希碼(hash code)就是根據(jù)實(shí)例數(shù)據(jù)計(jì)算出來(lái)的1個(gè)int值,如果兩個(gè)實(shí)例被認(rèn)為是equals,那末它們必須具有相同的hash code。因此,hash code**只能**根據(jù)equals()方法中所比較的變量域來(lái)計(jì)算,但其實(shí)不1定需要使用所有的域。

    2維平面上的點(diǎn)PointhashCode()實(shí)現(xiàn)以下:

    `public class Point {
        private int x, y;
    
    
    public boolean equals(Object other) {
        // see above
    }
    
    publice int hashCode() {
        return x;
    }
    

    }
    `

    上面的hashCode()實(shí)現(xiàn)時(shí)正確的(雖然不是最優(yōu)的),由于它依賴于equals()方法的比較域x

    固然,我們期望不同對(duì)象的hash code越分散越好。上面的實(shí)現(xiàn)中,所有具有相同x坐標(biāo)的點(diǎn)都具有相同的hash code,我們可以通過(guò)以下方法改進(jìn):

  • 使用多個(gè)變量的乘積;

  • 使用位運(yùn)算異或^
  • 給整數(shù)變量乘上1個(gè)質(zhì)數(shù)。

    `public class Point { 
    private int x, y;

    public boolean equals(Object other) {
        // see above
    }
    
    publice int hashCode() {
        return 31*x ^ 37*y;
    }
    

    }
    `

    但是,為了使hash code的計(jì)算速度快,1般不使用乘法。

    帶援用類型的hashCode()方法

    如果1個(gè)類包括了援用變量,那末就能夠使用援用變量的hashCode()方法。但是,跟equals()方法1樣,1定要注意援用是不是為null,否則可能拋出NullPointerException異常。對(duì)null的情況,可以返回1個(gè)固定值(這個(gè)固定值應(yīng)當(dāng)是正整數(shù),由于在1些hash map的實(shí)現(xiàn)中這些值可能有特殊的意義)。

    `public class Person {
        private String name;
        private Date birht;
    
    
    public boolean equals(Object other) {
        // see above
    }
    
    public int hashCode() {
        return (name == null ? 17 : name.hashCode()) 
            ^ (birht == null ? 31 : birth.hashCode());
    }
    

    }
    `

    equals()和hashCode()方法的默許實(shí)現(xiàn)

    這兩個(gè)方法的默許實(shí)現(xiàn)只適用于簡(jiǎn)單的情況。equals方法只有在與其本身比較時(shí)才返回truehashCode()方法依賴于單1對(duì)象的哈希(unique instance hash,如對(duì)象在內(nèi)存中的地址或不同虛擬機(jī)有不同的實(shí)現(xiàn))。

    由于hashCode()依賴于對(duì)象的唯1性(identity),所以只重寫equals()方法而不重寫hashCode()方法是毛病的。否則,兩個(gè)對(duì)象多是相等的,卻有不同的哈希值。如果實(shí)在沒有適合的哈希值計(jì)算方法,那末可以返回1個(gè)常數(shù)(比如7),這樣也比使用默許的實(shí)現(xiàn)好,雖然這樣會(huì)致使Map退化成List

    高級(jí)策略

    equals()hashCode()必須都盡量地快,由于它們常常被重復(fù)地調(diào)用。

    equals()方法的某些部份要比其他部份快,所以可以先比較快的部份。比如,基本數(shù)據(jù)類型的比較要快于援用類型的equals(),那末可以先比較基本數(shù)據(jù)類型。一樣地,如果兩個(gè)對(duì)象類型不同,那末就沒必要比較任何數(shù)據(jù)域了。

    只讀對(duì)象

    如果1個(gè)對(duì)象是只讀的(immutable),那末可以提早計(jì)算出它的哈希值。當(dāng)這樣的對(duì)象被創(chuàng)建時(shí),所有的值都會(huì)通過(guò)構(gòu)造函數(shù)傳入,這時(shí)候就能夠計(jì)算出它的哈希值。

    `public class Point {
        private final int x, y;
        private final int hashCode;
    
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
        this.hashCode = 31*x ^ 37*y;
    }
    
    public boolean equals(Object other) {
        // see above
    }
    
    public int hashCode() {
        return hashCode;
    }
    

    }
    `

    注意,上面代碼中的變量x和y都是final的,保證以后不會(huì)被改變,所以可以在創(chuàng)建對(duì)象時(shí)就計(jì)算出哈希值。

    為什么不用instanceof

    在Joshua Bloch非常著名的《Effective Java》1書中,他推薦使用instanceof來(lái)測(cè)試以決定對(duì)象的類型,表面上看這是1個(gè)很好的想法,實(shí)際上這有1個(gè)重大缺點(diǎn),由于instanceof不是對(duì)稱的。

    下面是Bloch推薦的做法:

    `public class BadPoint {
        private int x, y;
    
    
    public boolean equals(Object other) {
        if (other == this) return true;
        if (!(other instanceof BadPoint)) return false; // Bad!!!
        BadPoint point = (BadPoint)other;
        return (x == point.x &amp;&amp; y == point.y);
    }
    
    public int hashCode() {
        return x + y;
    }
    

    }
    `

    由于這個(gè)代碼更短,而且是《Effective Java》1書所推薦,所以已根深蒂固于很多Java程序員的編碼中。這同樣成為本書最具爭(zhēng)議的內(nèi)容之1。

    使用instanceof的最大問(wèn)題是它不具有對(duì)稱性,當(dāng)使用繼承時(shí)這個(gè)問(wèn)題將會(huì)體現(xiàn)出來(lái):

    `public class BadPoint3D extends BadPoint {
        private int z;
    
    
    public boolean equals(Object other) {
        if (!super.equals(other)) return false;
        if (!(other instanceof BadPoint3D)) return false; // Bad!!!
        BadPoint3D point = (BadPoint3D)other;
        return (z == point.z);
    }
    

    }
    `

    當(dāng)1個(gè)BadPoint對(duì)象和1個(gè)BadPoint3D對(duì)象比較時(shí)就會(huì)出現(xiàn)問(wèn)題,badPoint instanceof badPoint3D == false,而point3D instanceof badPoint == true

    完全的寫法

    Point

    `public class Point {
        private static double version = 1.0;
        private transient double distance;
    
    
    private String name;
    private int x, y;
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public Point(String name, int x, int y) {
        this(x, y);
        this.name = name;
    }
    
    public boolean equals(Object other) {
        if (other == this) return true;
        if (other == null) return false;
        if (getClass() != other.getClass()) return false;
        Point point = (Point)other;
        return (x == point.x &amp;&amp; y == point.y 
            &amp;&amp; (name = point.name || (name != null &amp;&amp; name.equals(point.name))));
    }
    
    public int hashCode() {
        return x ^ y;
    }
    

    }
    `

    Point3D

    `public class Point3D {
        private int z;
    
    
    public Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
    
    public Point3D(String name, int x, int y, int z) {
        super(name, x, y);
        this.z = z;
    }
    
    public boolean equals(Object other) {
        if (!super.equals(other)) return false;
        Point3D point = (Point3D)other;
        return (z == point.z);
    }
    
    public int hashCode() {
        return super.hashCode() ^ z;
    }
    

    }

更深的學(xué)習(xí)可以參看 如何在Java中避免equals方法的隱藏圈套 | 酷 殼 - CoolShell.cn

說(shuō)明:本文譯自javaword.com,如果侵權(quán),請(qǐng)聯(lián)系站長(zhǎng)刪除。同時(shí),如果轉(zhuǎn)載本文,請(qǐng)注明本文鏈接。

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 国产成人久久精品一区二区三区 | 韩日精品视频 | 在线视频97 | 久久电影天堂 | 亚洲三区视频 | 免费欧美日韩 | 亚洲午夜网 | 美女被免费喷白浆视频 | 麻豆b2b | 久久伊人影院 | 欧美成人一区二区三区片免费 | 成人免费98调教 | 国产区在线 | 国产午夜精品一区二区三区欧美 | 精品国产欧美一区二区三区成人 | 亚洲人成网亚洲欧洲无码 | 一区二区三区久久 | 日产精品久久久久久久性色 | 日韩免费一区二区三区 | 日韩成人影院 | 在线观看成人av | 国产97在线观看 | 日韩中文字幕第一页 | 亚洲电影免费观看 | 亚洲欧洲精品在线 | 福利毛片 | 国产在线小视频 | 成人黄色片在线观看 | 欧美a级成人淫片免费看 | 青青av在线 | 简单av网| 国产黄色电影 | 久久tv | 操操操网站 | 三级电影免费观看 | 99久在线视频 | 亚洲福利在线观看 | 欧美一区二区日韩 | 黄色网入口| 国产精品久久久久久久久久免 | 在线免费毛片 |