java中判斷兩個對象是否相等

如何判斷兩個對象相等,這個問題實際上可以看做是如何對equals方法和hashcode方法的理解。
從以下幾個點來理解equals和hashCode方法:

1、equals的作用及與==的區別。
2、hashcode的作用及與equals的關係。

1、equals的作用及與== 的區別。
equals被用來判斷兩個對象是否相等。
equals通常用來比較兩個對象的內容是否相等,用來比較兩個對象的地址是否相等。
equals方法默認等同於“”
Object類中的equals方法定義爲判斷兩個對象的地址是否相等(可以理解成是否是同一個對象),地址相等則認爲是對象相等。這也就意味着,我們新建的所有類如果沒有複寫equals方法,那麼判斷兩個對象是否相等時就等同於“==”,也就是兩個對象的地址是否相等。
Object類中equals的方法實現如下:

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

但在我們的實際開發中,通常會認爲兩個對象的內容相等時,則兩個對象相等,equals返回true。對象內容不同,則返回false。
所以可以總結爲兩種情況
1、類未複寫equals方法,則使用equals方法比較兩個對象時,相當於==比較,即兩個對象的地址是否相等。地址相等,返回true,地址不相等,返回false。
2、類複寫equals方法,比較兩個對象時,則走複寫之後的判斷方式。通常,我們會將equals複寫成:當兩個對象內容相同時,則equals返回true,內容不同時,返回false。
舉個例子:

public class EqualTest {

    public static void main(String[] args) {

        Person p1 = new Person(10,"張三");

        Person p2 = new Person(10,"張三");

        System.out.println(p1.equals(p2));

    }

}

class Person{

    int age;

    String name;

    public Person(int age, String name) {

        super();

        this.age = age;

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}

Person未複寫equals方法,則默認使用了Object中的equals,即爲兩個對象(p1和p2)的內存地址判斷,p1和p2很明顯內存地址不同,所以輸出結果很明顯爲false。
如果我們複寫equals方法呢?我們認爲名字和年齡一樣的就是同一個人,那麼p1和p2都表示10歲的張三,這兩個對象應該是相等的。複寫的equals方法如下:

    @Override

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        Person other = (Person) obj;

        if (age != other.age)

            return false;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

同樣的,執行上述用例,得到的結果是true。
BTW:如果equals方法返回true,那麼是否也是true?
不一定是true。equals返回true有兩種可能,一種是兩個對象地址相同,一種是兩個對象內容相同。當內容相同時,地址可能不同,所以比較的結果可能爲false。
我們把main方法加上對==的判斷,如下:

public static void main(String[] args) {

        Person p1 = new Person(10,"張三");

        Person p2 = new Person(10,"張三");

        System.out.println(p1.equals(p2));

        System.out.println(p1 == p2);

}

輸出結果很明顯 p1==p2的結果是false。

補充Java中對Equals的要求:

  1. 對稱性:如果x.equals(y)返回是"true",那麼y.equals(x)也應該返回是"true"。
  2. 反射性:x.equals(x)必須返回是"true"。
  3. 類推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那麼z.equals(x)也應該返回是"true"。
  4. 一致性:如果x.equals(y)返回是"true",只要x和y內容一直不變,不管你重複x.equals(y)多少次,返回都是"true"。
  5. 非空性,x.equals(null),永遠返回是"false";x.equals(和x不同類型的對象)永遠返回是"false"。

2、hashCode的作用及與equals的關係。
hashCode的作用是用來獲取哈希碼,也可以稱作散列碼。實際返回值爲一個int型數據。用於確定對象在哈希表中的位置。
Object中有hashcode方法,也就意味着所有的類都有hashCode方法。
但是,hashcode只有在創建某個類的散列表的時候纔有用,需要根據hashcode值確認對象在散列表中的位置,但在其他情況下沒用。
java中本質上是散列表的類常見的有HashMap,HashSet,HashTable
所以,如果一個對象一定不會在散列表中使用,那麼是沒有必要複寫hashCode方法的。但一般情況下我們還是會複寫hashCode方法,因爲誰能保證這個對象不會出現再hashMap等中呢?
舉個例子:
兩個對象equals相等的時候,hashcode並不一定相等。

public class EqualTest {

    public static void main(String[] args) {

        Person p1 = new Person(10, "張三");

        Person p2 = new Person(10, "張三");

        System.out.println(

                "p1.equals(p2)=" + p1.equals(p2) + ", p1.hashcode=" + p1.hashCode() + ", p2.hashcode=" +p2.hashCode());

    }

}

class Person {

    int age;

    String name;

    public Person(int age, String name) {

        super();

        this.age = age;

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        Person other = (Person) obj;

        if (age != other.age)

            return false;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

}

Person沒有複寫hashCode方法,使用Object默認的hashCode實現,輸出結果如下:

p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203

從結果可以看出,equals雖然相同,但是p1和p2的hashcode並不相同。

如果Person用於散列表的類中呢,這裏用HashSet來做測試。

public class EqualTest {

    public static void main(String[] args) {

        Person p1 = new Person(10, "張三");

        Person p2 = new Person(10, "張三");

        System.out.println(

                "p1.equals(p2)=" + p1.equals(p2) + ", p1.hashcode=" + p1.hashCode() + ", p2.hashcode=" +p2.hashCode());

        HashSet<Person> set = new HashSet<Person>();

        set.add(p1);

        set.add(p2);

        System.out.println(set);

    }

}

class Person {

    int age;

    String name;

    public Person(int age, String name) {

        super();

        this.age = age;

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        Person other = (Person) obj;

        if (age != other.age)

            return false;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

    @Override

    public String toString() {

        return "Person [age=" + age + ", name=" + name + "]";

    }

}

輸出結果

p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203

[Person [age=10, name=張三], Person [age=10, name=張三]]

 

p1和p2的equals相同,我們認爲是兩個對象相等,但是這兩個對象竟然同時出現再hashSet中,hashSet中是不會出現兩個相同的元素的。
那問題在哪裏?
hashset在添加一個元素的時候,會做如下判斷:
1、如果添加元素的hashcode相等並且 對象equals爲true或對象== 時,則認爲是同一個元素,不添加到新元素中。
2、如果不符合上述條件,則認爲是一個新元素,添加到set中。
所以,雖然p1和p2equals比較時相等,但是hashcode並不一樣,所以在往set中添加的時候認爲是兩個不同的元素,所以纔會出現了p1和p2同時在set中的情況。

我們改進下,複寫一下hashcode方法,如下:

class Person {

    int age;

    String name;

    public Person(int age, String name) {

        super();

        this.age = age;

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public int hashCode() {

        final int prime = 31;

        int result = 1;

        result = prime * result + age;

        result = prime * result + ((name == null) ? 0 : name.hashCode());

        return result;

    }

    @Override

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        Person other = (Person) obj;

        if (age != other.age)

            return false;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

    @Override

    public String toString() {

        return "Person [age=" + age + ", name=" + name + "]";

    }

}

重新執行結果:

p1.equals(p2)=true, p1.hashcode=776160, p2.hashcode=776160

[Person [age=10, name=張三]]

  •  

於是看到set中僅有一個Person值了。

補充幾點:
1、新建一個類,尤其是業務相關的對象類的時候,最好複寫equals方法。
2、複寫equals方法時,同時記着要複寫hashCode方法,誰能保證說這個對象一定不會出現在hashMap中呢?如果你用的是eclipse的自動代碼生成,你會發現eclipse中複寫equals和hashCode是在一起的。

引申出幾個經常在面試中問到的問題:
1、兩個對象,如果a.equals(b)==true,那麼a和b是否相等?
相等,但地址不一定相等。
2、兩個對象,如果hashcode一樣,那麼兩個對象是否相等?
不一定相等,判斷兩個對象是否相等,需要判斷equals是否爲true。

注意:
是否可以直接根據hashcode值判斷兩個對象是否相等嗎?肯定是不可以的,因爲不同的對象可能會生成相同的hashcode值。雖然不能根據hashcode值判斷兩個對象是否相等,但是可以直接根據hashcode值判斷兩個對象不等,如果兩個對象的hashcode值不等,則必定是兩個不同的對象。如果要判斷兩個對象是否真正相等,必須通過equals方法。

也就是說對於兩個對象,如果調用equals方法得到的結果爲true,則兩個對象的hashcode值必定相等;

如果equals方法得到的結果爲false,則兩個對象的hashcode值不一定不同;

如果兩個對象的hashcode值不等,則equals方法得到的結果必定爲false;

如果兩個對象的hashcode值相等,則equals方法得到的結果未知。

 

發佈了11 篇原創文章 · 獲贊 3 · 訪問量 5561
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章