天天看點

【計算機基礎】HashCode And EqualsObject類下的equals和hashCode:== 和 equalshashCode碼的特點為什麼重寫equals建議一定要重寫hashCode?

目錄:

  1. Object類下的equals和hashCode:
  2. == 和 equals
  3. hashCode碼的特點
  4. 為什麼重寫equals建議一定要重寫hashCode?

Object類下的equals和hashCode:

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

== 和 equals

對于值對象,比較的是兩個對象的值

對于引用對象, 比較的是兩個對象的位址

如上代碼Object類下的equals 通過==對比是目前對象和傳入對象是否是同一個對象,對比的是兩個對象的位址

也就是即使是對象裡面的值都是一樣的,也會被判,這兩對象不相等。通過代碼來了解一下這句話:

public class Cat {
    int color,weight,height;

    public Cat(int color,int weight,int height){
        this.color = color;
        this.weight = weight;
        this.height = height;
    }
}
           
public static void main(String[] args) {
        Cat c1 = new Cat(1, 1, 1);
        Cat c2 = new Cat(1, 1, 1);
		
        System.out.println("c1==c2的結果是:"+(c1==c2));
        System.out.println("c1.equals(c2)的結果是:"+c1.equals(c2));

        System.out.println("c1.hashCode="+c1.hashCode());
        System.out.println("c2.hashCode="+c2.hashCode());
}
           

c1==c2的結果是:false

c1.equals(c2)的結果是:false

c1.hashCode=356573597

c2.hashCode=1735600054

通過結果可以知道:即使是一樣的屬性的Cat對象,hashcode和equals都是不同的。

引用equals調用的是object的equals方法,通過thisobject判斷目前對象和傳入的對象也就是c1和c2的引用是否是同一個,答案當然是不一樣,是以equals的結果和的結果是一樣的,都是false。

不同對象他們的hashCode是不一樣的。

但思考:不同對象的hashCode一定是不一樣的?

hashCode碼的特點

上面提到,不同對象的hashCode一定是不一樣的?—不是

那麼反過來呢?

一樣的hashCode,一定是同一個對象嗎?----不是

相同對象的hashCode一定是一樣的嗎?-----是

TODO:深入原理不太明白,看源碼注釋可以看到:

/**
     * 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&trade; 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();
           
  • 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.
 *   如果根據{@code equals(Object)}兩個對象相等方法,然後對每個
           

這兩個對象必須産生相同的整數結果

為什麼重寫equals建議一定要重寫hashCode?

hashCode的計算基礎是根據equals來計算的。如上,相同對象的hashcode一定是一樣的。

在使用hashCode和equals的時候,是通過先對比hashCode,看hashCode如果相同,再對比equals,如果equals的結果是true,則表示兩個對象一樣的。如果hashCode不同,那麼就認定這兩個對象不同了。

是以,如果你更換的equals的對比規則,但是沒有更新hashCode的計算規則,即使equals是true,但是hashCode還是按照this==object的規則進行對比的,那麼存儲的時候hashCode的值也是按照不同對象去存儲的,是以會出現本來是相同對象,卻按照兩個不同對象取出的情況。

舉個例子:

HashSet,set集合的特定就是存儲的對象是惟一的

public static void main(String[] args) {
        HashSet<RectObject> set = new HashSet<RectObject>();
        RectObject r1 = new RectObject(3,3);
        RectObject r2 = new RectObject(5,5);
        RectObject r3 = new RectObject(3,3);
        System.out.println("r1.equals(r3)的結果:"+r1.equals(r3));
        System.out.println("r1.hashCode()"+r1.hashCode());
        System.out.println("r3.hashCode()"+r3.hashCode());
        
        set.add(r1);
        set.add(r2);
        set.add(r3);
        System.out.println("Set集合的size:"+set.size());
    }
           

r1.equals(r3)的結果:true

r1.hashCode()356573597

r3.hashCode()1735600054

Set集合的size:3

從結果可以看到,雖然r1和r3的結果是true, 但是Set集合卻當做兩個不同的對象插入了。

原因在于:

RectObject類中重寫了equals方法,沒有重寫hashCode方法

public class RectObject {
    public int x;
    public int y;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RectObject that = (RectObject) o;
        return x == that.x &&
                y == that.y;
    }

//    @Override
//    public int hashCode() {
//        return Objects.hash(x, y);
//    }
}
           

重寫了hashCode之後的運作結果:

r1.equals(r3)的結果:true

r1.hashCode()1057

r3.hashCode()1057

Set集合的size:2

繼續閱讀