equals使用及重寫equals方法爲什麼要重寫hashCode?

一. equals的使用

用來判斷兩個對象是否相等。

  • 類如果沒有重寫Object的equals方法,則調用Object的equals方法,比較的是對象在堆中的地址是否相等
  • 類如果重寫了equals方法,一般用來比較對象的屬性是否相等
二. 爲什麼要重寫equals?

爲什麼要重寫equals方法?主要看使用場景,如果不需要進行對象比較就不需要重寫。
舉個栗子,用戶要修改用戶名,傳了個User對象過來,此時從DB裏取出舊的User對象,比較兩個是不是相等的(其實是比較用戶名),如果沒有重寫equals方法,用的是Object的equals方法,比較對象的地址,與我們的意願相違背。

例子1

public class Main {
    public static void main(String[] args) {
        User newUser = new User("dkangel");
        User dbUser = new User("dkangel");
        System.out.println(newUser.equals(dbUser));
    }
}

1. 未重寫equals方法,結果爲false

public class User {
    private String name;
    
    public User(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

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

2. 重寫equals方法,結果爲true

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    /**
     * 重寫equals方法,比較name屬性是否相等
     *
     * @param obj 待比較對象
     * @return boolean
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof User)) {
            return false;
        }
        User user = (User) obj;
        return Objects.equals(user.name, this.getName());
    }
}
三. 重寫了equals方法一定要重寫hashCode方法嗎?爲什麼?

hashCode方法的作用是獲取哈希碼(也稱散列碼),哈希碼的作用是確定對象在哈希表中的位置。

先看下 hashCode 與 equals 的相關規定

  • 如果兩個對象相等,則 hashcode 一定也是相同的
  • 兩個對象相等,對兩個對象分別調用 equals 方法都返回 true
  • 兩個對象有相同的 hashcode 值,它們也不一定是相等的
  • hashCode方法的默認行爲是對堆上的對象產生獨特值。如果沒有重寫 hashCode方法,則該類的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)

從約定上看:重寫equals方法必須重寫hashCode方法。

從使用上看:

  • 如果類在使用過程中不需要創建類對應的哈希表(就是不會在HashMap/HashSet/HashTable等用到hash的數據數據裏用到該類),那就與hashCode方法沒什麼關係,也就不需要重寫hashCode方法。如上面的例子1,只重寫了equals方法,沒有重寫hashCode方法,並不影響使用。

  • 如果類在使用過程中需要創建類對應的哈希表,那就需要重寫hashCode方法,爲什麼?再舉個栗子

例子2

當你把對象加入 HashSet 時,HashSet 會先計算對象的 hashcode 值來判斷對象加入的位置,同時也會與該位置其他已經加入的對象的 hashcode 值作比較,如果沒有相符的 hashcode,HashSet 會假設對象沒有重複出現。但是如果發現有相同 hashcode 值的對象,這時會調用 equals()方法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet 就不會讓其加入操作成功。如果不同的話,就會重新散列到其他位置。

public class Main {
    public static void main(String[] args) {
        User newUser = new User("dkangel");
        User dbUser = new User("dkangel");
        User otherUser = new User("zhangsan");

        HashSet<User> users = new HashSet();
        users.add(newUser);
        users.add(dbUser);
        users.add(otherUser);

        System.out.println("users size: " + users.size());
        users.forEach(user -> System.out.printf("name: %s, hashCode: %s\n", user.getName(), user.hashCode()));
    }
}

1. 未重寫hashCode方法
結果爲3,兩個name爲“dkangel”的對象由於未重寫hashCode方法,導致set中出現重複值。
在這裏插入圖片描述
2. 重寫hashCode方法

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    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 instanceof User)) {
            return false;
        }
        User user = (User) obj;
        return Objects.equals(user.name, this.getName());
    }

    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

結果爲2,重寫了hashCode方法兩個name爲“dkangel”的對象只添加了一個到set裏。
在這裏插入圖片描述

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