1. 程式人生 > 其它 >hashCode()與equals()

hashCode()與equals()

有面試官會問:你重寫過 hashcode和equals 麼,為什麼重寫equals時必須重寫hashCode方法?
equals和hashCode都是Object物件中的非final方法,它們設計的目的就是被用來覆蓋(override)的,所以在程式設計中還是經常需要處理這兩個方法.下面我們一起來看一下,它們到底有什麼區別,總結一波!

hashCode介紹

hashCode()的作用是獲取雜湊碼,也稱為雜湊碼,它實際上是返回一個int整數,這個雜湊碼的作用是確定該物件在雜湊表中的索引位置,hashCode()定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode() 函式.

equals介紹

equals()它的作用也是判斷兩個物件是否相等,
如果物件重寫了equals()方法,一般比較兩個物件的內容是否相等,
如果沒有重寫,比較兩個物件的地址是否相同,價於“==”,
同樣的,equals()定義在JDK的Object.java中,這就意味著Java中的任何類都包含有equals()函式.

hashCode() 和 equals() 有什麼關係?

如果該類不會在HashSet、 HashTable、 HashMap等這些本質是散列表的資料結構中用到,則hashCode()和equals()兩者之間是沒有關係的。 
下面我們分析一下該類會在HashSet、 HashTable、 HashMap等這些本質是散列表的資料結構中用到時的關係:
equals相等的兩個物件的hashcode一定相等;
equals不相等的兩個物件的hashcode有可能相等.

示例程式碼

點選檢視程式碼
package com.yuanxiaohao;
import java.util.*;
public class TestHashCode {
    public static void main(String[] args) {
        test();
    }
    public static void test(){
        System.out.println("######test######");
        Set<Model> set =new HashSet<Model>();
        Model a =new Model("猿小豪","20");
        Model b =new Model("猿小豪","20");
        set.add(a);
        System.out.println("a.hashCode:"+a.hashCode());
        set.add(b);
	  System.out.println("b.hashCode:"+b.hashCode());
        System.out.println(set);
    }
    public static class Model {
        private String name;
        private String age;
        public Model(String name, String age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAge() {
            return age;
        }
        public void setAge(String age) {
            this.age = age;
        }
	  @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Model)) return false;
            Model model = (Model) o;
            return name.equals(model.name) &&
                    age.equals(model.age);
        }
//    @Override
//    public int hashCode() {
//        return (name+age).hashCode();
//    }
        @Override
        public String toString() {
            return "Model{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    }
}

執行結果

######test2######
a.hashCode:1163157884
b.hashCode:1956725890
[Model{name='猿小豪', age='20'}, Model{name='猿小豪', age='20'}]

結果分析

我們重寫了Model的equals(),但是很奇怪的發現HashSet中仍然有重複元素:a和b.

為什麼會出現這種情況呢?

這是因為雖然a和b的內容相等,但是它們的hashCode()不等,所以HashSet在新增a和b的時候,認為它們不相等.

HashSet的add方法原始碼分析

HashSet中的add方法實際呼叫的是HashMap中的put方法,而HashMap的put方法中,使用key作為引數,在hash方法中用key的hashCode()計算出hash值,然後在putVal方法中使用計算出的hash值進行比較,如果hash值相同,再通過equals()比較key值是否相同,如果都相同,則覆蓋原有的Node物件,否則建立新的Node物件放入連結串列中.

總結

如果類使用在散列表的集合物件中,要判斷兩個物件是否相同,除了要重寫equals()之外,也要按照規則重寫hashCode()函式,否則會出現”equals相等的兩個物件的hashcode不相等”的情況,從而在使用相關集合時,引起與自己的預期不相符的情況.

補充

至於為什麼先使用hash值判斷再使用equals.
個人理解:
在計算存放陣列下標的過程中,hash值已經通過hashCode計算得到了,後面就可以直接拿來用了,這樣就減少了equals次數,提高了效率.