java中 ==和equals和hashCode的區別

面試的時候,經常被問到,總結一下,如有不正確的地方,多多指教。

1 、關於“==”

關於“!=”,看完下面的內容,大家應該可以自己腦補;“==”可以進行下面三種類型的比較;

  • 數值類型==

可以在基本類型之間進行比較,比如整型類型,int,long;浮點類型float,double;
(1)基本類型之間可以相互比較,如果對應的值相等,則返回true,否則,返回false;
(2)基本類型與對應的包裝類型之間也可以進行比較,比較的時候,包裝類型要進行拆箱操作
(3) 布爾類型與數值類型(整型、浮點型)是不能直接用“==”比較的。

示例代碼:

  public static void main(String []args){
        int i = 9;
        float f = 9f;
        long l = 9l;
        double d = 9d;
        Long L = new Long(9l);
        double D = new Double(9d);
        double D1 = new Double(9.1d);

        System.out.println("int == float : " +(i==f));
        System.out.println("int == long : " +(i==l));
        System.out.println("int == double : " +(i==d));
        System.out.println("Long == Double : " +(L==D));
        System.out.println("Long != Double : " +(L==D1));
    }

運行結果:

int == float : true
int == long : true
int == double : true
Long == Double : true
Long != Double : false
  • 布爾類型==

(1) 兩個操作數操作數類型爲boolean,或者是其包裝類型Boolean;
(2) 如果其中一個操作數是類型Boolean,則進行拆箱轉換。
(3) 如果兩個操作數都爲true或者false,結果爲true,否則,結果爲false;

示例代碼:

    public static void main(String []args){
        boolean t = true;
        Boolean t1 = true;
        boolean f = false;
        Boolean f1 = false;
        System.out.println("boolean t == Boolean  t1 : " +(t==t1));
        System.out.println("boolean t == Boolean  f  : " +(t==f));
        System.out.println("boolean f == Boolean  f1 : " +(f==f1));
        System.out.println("Boolean t1 == Boolean f1 : " +(t1==f1));
    }

運行結果:

boolean t == Boolean  t1 : true
boolean t == Boolean  f  : false
boolean f == Boolean  f1 : true
Boolean t1 == Boolean f1 : false
  • 引用類型

運算符的操作數是引用類型或者是null類型,引用類型可以是對象或者數組;
(1)在比較時,如果兩個操作數都是null或者都引用同一個對象或數組,則結果爲true,否則,結果爲false;
(2)雖然“==”可以用於比較類型的引用String,但是這樣的相等性測試是確定兩個操作數是否引用相同的String 對象;因此,可能會出現兩個new的String對象字符串序列相同,但是“==”結果卻是false的情況;
(3)兩個字符串的內容s和t是否相等可以通過s.equals(t)方法進行計較。

示例代碼:

    public static void main(String []args){
        Object o1 = new Object();
        Object o2 = o1; //o2與o1引用同一對象
        Object o3 = new Object();
        Object o4 = null;
        Object o5 = null;
        System.out.println("o1 == o2 : " +(o1==o2));
        System.out.println("o1 == o3 : " +(o1==o3));
        System.out.println("o1 == o4 : " +(o1==o4));
        System.out.println("o4 == o5 : " +(o4==o5));

    }

運行結果:

o1 == o2 : true
o1 == o3 : false
o1 == o4 : false
o4 == o5 : true

2 、關於“equals”

boolean equals​(Object obj) 方法來自java.lang.Object類,代碼如下:

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

是不是感覺有點眼熟,實際上,如果某個類沒有覆蓋equals()方法,當它的通過equals()比較兩個對象時,是在比較兩個對象是不是同一個對象。這時,等價於通過“==”去比較這兩個對象。

我們可以覆蓋類的equals()方法,來讓equals()通過其它方式比較兩個對象是否相等。通常的做法是:若兩個對象的內容相等,則equals()方法返回true;否則,返回false;

由於 Java類,String 、Math、Integer、Double等這些封裝類在使用equals()方法時,已經覆蓋了object類的equals()方法,因此,是比較對象的內容是否相等;自定義類的話就需要自己覆蓋equals()方法來判斷兩個對象的內容是否相等。

示例代碼:

package com.example.demo;

import com.sun.tools.doclets.formats.html.SourceToHTMLConverter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;

public class EqualsTest {
    public static void main(String []args){

        Person p1 = new Person("金毛獅王",100);
        Person p2 = new Person("金毛獅王",100);
        System.out.println("未覆蓋equals:  "+p1.equals(p2));

        Person1 p3 = new Person1("金毛獅王",100);
        Person1 p4 = new Person1("金毛獅王",100);
        System.out.println("覆蓋equals:   "+p3.equals(p4));
    }
}

class Person{
    String name;
    int age;

    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;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Person1{
    String name;
    int age;

    public Person1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //覆蓋之後,如果兩個對象的內容相同,或者是屬於同一個對象的引用,都返回true,否則,返回false
    @Override
    public boolean equals(Object obj) {
        if(obj == null)
            return false;
        //如果是同一個對象返回true
        if(this == obj)
            return true;
        //判斷是否類型相同
        if(this.getClass()!=obj.getClass())
            return false;
        Person1 p = (Person1)obj;
        return this.name == p.name && this.age == p.age;
    }
}

運行結果:

未覆蓋equals:  false
覆蓋equals:   true

equals方法在非空(null)對象引用上有如下的等價關係:

  • 自反性: 對於任何非空引用值x , x.equals(x)應該返回true;
  • 對稱性:對於任何非空引用值x和y,如果x.equals(y) 返回true,那麼 y.equals(x)也一定返回true;
  • 傳遞性:對於任何非空引用值x,y,z,如果x.equals(y)返回true,y.equals(z)返回true, 那麼x.equals(z)也一定返回true;
  • 一致性:對於任何非空引用值 x和y,在多次調用 x.equals(y)始終返回true 或始終返回false,除非equals的比較對象被修改(這裏多說一句,所謂的“修改”就是說你在覆蓋equals方法時,進行對象內容比較,用到的對象中的某個成員的值發生了改變)。
  • 對於任何非空引用值x, x.equals(null)應返回false。

3 、關於“hashCode”

public int hashCode()也是來自java.lang.Object類,它返回對象的哈希碼(也叫散列碼,一個整型數字);

哈希碼是用來確定該對象在哈希表中的索引位置,在Java集合中,HashMap,HashTable,HashSet等都屬於哈希(散列)表;因此,如果當前對象不會在散列表中被用到(就是不會作爲HashSet的元素,或者HashMap的Key),那麼完全可以忽略hashCode;可能你現在在想我平時用HashMap的時候也沒有考慮hashCode的問題啊,不也用的好好的;因爲,一般情況下,都用String或者Java已有類型作爲Key,已有類型在默認繼承Object的時候已經覆蓋了hashCode方法,因此在使用HashSet或者HashMap的時候,不會出現重複元素;看幾個例子就明白了:

下面分別用HashSet和HashMap做測試,要做的就是保證HashSet裏面元素不同,HashMap的key也不相同;

先看HashSet,
示例代碼:

package com.example.demo;

import java.util.HashSet;
import java.util.Iterator;

public class HashTest {

    public static void main(String[] args) {
        HashSet<MyString>  stringHashSet = new HashSet<>();
        HashSet<MyString1> myStringHashSet = new HashSet<>();
        stringHashSet.add(new MyString("hello"));
        stringHashSet.add(new MyString("hello"));
        stringHashSet.add(new MyString("hello1"));
        Iterator it = stringHashSet.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("===========");
        myStringHashSet.add(new MyString1("hello"));
        myStringHashSet.add(new MyString1("hello"));
        myStringHashSet.add(new MyString1("hello1"));
        Iterator<MyString1> it1 = myStringHashSet.iterator();
        while(it1.hasNext()){
            System.out.println(it1.next());
        }
    }
}

class MyString{

    String value;

    public MyString(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return value;
    }
}

class MyString1{

    String value;

    public MyString1(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return value;
    }

    @Override
    public int hashCode() {
        return value.toUpperCase().hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        MyString1 s = (MyString1)obj;
        return this.value ==s.value;
    }
}
  • 運行結果
hello
hello
hello1
===========
hello1
hello

可以看出在沒有覆蓋hashcode()方法時,HashSet中的元素可能會出現重複的情況,這就違背了Set集合的設計初衷,而覆蓋了hashcode()方法,HashSet中的元素就不重複了;

原因就是一般情況下,不同的對象會產生不同的hashcode,即使對象的內容相同;按照hashcode的約定規則:如果兩個對象equals()相等,那麼應該有相同的hashcode,因此,覆蓋後的hashcode()要符合這一規則。

hashcode()的生成有如下的約定規則:

  • 在Java應用程序執行期間,多次在同一對象上調用它時,hashCode()必須始終返回相同的整數,除非當前對象的內容被修改;從應用程序的一次執行到同一應用程序的另一次執行,該整數不需要保持一致。
  • 如果兩個對象equals()後相等,它們在使用hashcode()方法後,生成的hashcode(散列碼)應該相同(這就解釋瞭如果要覆蓋equals()方法的話,就要覆蓋hashcode()方法)。
  • 如果兩個對象equals()後不相等,它們在使用hashCode()方法後,產生的散列碼可以相同,也可以不同;但是,要明白:爲不同的對象生成不同的hashcode(散列碼)能夠改善散列表的性能。

HashMap的例子不貼了,大家可以自己試一下,只要保證key滿足上述特性就可以了;

關於散列表的內容,數據結構與算法(要找經典教材看)中有詳細介紹,不太提倡看博客,有時候大神的思路不一定你能跟的上,書上的內容更加全面一些,更通俗易懂。

參考資料

https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.21
https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals(java.lang.Object)
https://www.cnblogs.com/skywang12345/p/3324958.html
https://www.jianshu.com/p/1acdfac2b4e4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
http://www.cnblogs.com/skywang12345/p/3311899.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章