HashCode和equal方法

uals()反映的是對象或變量具體的值,即兩個對象裏面包含的值--可能是對象的引用,也可能是值類型的值。

而hashCode()是對象或變量通過哈希算法計算出的哈希值。

之所以有hashCode方法,是因爲在批量的對象比較中,hashCode要比equals來得快,很多集合都用到了hashCode,比如HashTable。

 

兩個obj,如果equals()相等,hashCode()一定相等。

兩個obj,如果hashCode()相等,equals()不一定相等(Hash散列值有衝突的情況,雖然概率很低)。

所以:

可以考慮在集合中,判斷兩個對象是否相等的規則是:

第一步,如果hashCode()相等,則查看第二步,否則不相等;

第二步,查看equals()是否相等,如果相等,則兩obj相等,否則還是不相等。

 

1、首先equals()和hashcode()這兩個方法都是從object類中繼承過來的。

equals()是對兩個對象的地址值進行的比較(即比較引用是否相同)。

hashCode()是一個本地方法,它的實現是根據本地機器相關的。

2、Java語言對equals()的要求如下,這些要求是必須遵循的:

A 對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”。

B 反射性:x.equals(x)必須返回是“true”。

C 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”。

D 一致性:如果x.equals(y)返回是“true”,只要x和y內容一直不變,不管你重複x.equals(y)多少次,返回都是“true”。

任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同類型的對象)永遠返回是“false”。

3、equals()相等的兩個對象,hashcode()一定相等;

反過來:hashcode()不等,一定能推出equals()也不等;

hashcode()相等,equals()可能相等,也可能不等。 

 

引用:http://blog.sina.com.cn/s/blog_59e0c16f0100xne7.html

1、爲什麼要重載equal方法?

答案:因爲Object的equal方法默認是兩個對象的引用的比較,意思就是指向同一內存,地址則相等,否則不相等;如果你現在需要利用對象裏面的值來判斷是否相等,則重載equal方法。

2、 爲什麼重載hashCode方法?

答案:一般的地方不需要重載hashCode,只有當類需要放在HashTable、HashMap、HashSet等等hash結構的集合時纔會重載hashCode,那麼爲什麼要重載hashCode呢?就HashMap來說,好比HashMap就是一個大內存塊,裏面有很多小內存塊,小內存塊裏面是一系列的對象,可以利用hashCode來查找小內存塊hashCode%size(小內存塊數量),所以當equal相等時,hashCode必須相等,而且如果是object對象,必須重載hashCode和equal方法。

3、 爲什麼equals()相等,hashCode就一定要相等,而hashCode相等,卻不要求equals相等?

答案:1、因爲是按照hashCode來訪問小內存塊,所以hashCode必須相等。

2、HashMap獲取一個對象是比較key的hashCode相等和equal爲true。

之所以hashCode相等,卻可以equal不等,就比如ObjectA和ObjectB他們都有屬性name,那麼hashCode都以name計算,所以hashCode一樣,但是兩個對象屬於不同類型,所以equal爲false。

4、 爲什麼需要hashCode?

1、 通過hashCode可以很快的查到小內存塊。
2、通過hashCode比較比equal方法快,當get時先比較hashCode,如果hashCode不同,直接返回false。

 

hashCode()的作用

1.hashcode是用來查找的,如果你學過數據結構就應該知道,在查找和排序這一章有
例如內存中有這樣的位置
0     1     2     3     4     5     6     7    
而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那麼當查找時就需要到這八個位置裏挨個去找,或者用二分法一類的算法。
但如果用hashcode那就會使效率提高很多。
我們這個類中有個字段叫ID,那麼我們就定義我們的hashcode爲ID%8,然後把我們的類存放在取得得餘數那個位置。比如我們的ID爲9,9除8的餘數爲1,那麼我們就把該類存在1這個位置,如果ID是13,求得的餘數是5,那麼我們就把該類放在5這個位置。這樣,以後在查找該類時就可以通過ID除8求餘數直接找到存放的位置了。

2.但是如果兩個類有相同的hashcode怎麼辦那(我們假設上面的類的ID不是唯一的),例如9除以8和17除以8的餘數都是1,那麼這是不是合法的,回答是:可以這樣。那麼如何判斷呢?在這個時候就需要定義   equals了。
也就是說,我們先通過   hashcode來判斷兩個類是否存放某個桶裏,但這個桶裏可能有很多類,那麼我們就需要再通過   equals   來在這個桶裏找到我們要的類。
那麼。重寫了equals(),爲什麼還要重寫hashCode()呢?
想想,你要在一個桶裏找東西,你必須先要找到這個桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什麼用啊
3。你要對A類排序,有兩種方法,一種就是讓A類實現comparabole結構並實現compareTo()方法,那麼可以通過Collections.sort(List    list)對其進行排序
另一種方法:自己定義一個類B實現Comparator類並實現compare方法,然後通過Collections.sort(List list,B b)進行排序

hashCode()是用來產生哈希瑪的,而哈希瑪是用來在散列存儲結構中確定對象的存儲地址的,(這一段在   Java編程思想   中講的很清楚的)象util包中的帶hash的集合類都是用這種存儲結構:HashMap,HashSet, 他們在將對象存儲時(嚴格說是對象引用),需要確定他們的地址吧,而HashCode()就是這個用途的,一般都需要重新定義它的,因爲默認情況下,由 Object類定義的 hashCode 方法會針對不同的對象返回不同的整數,這一般是通過將該對象的內部地址轉換成一個整數來實現的,現在舉個例子來說,就拿HashSet來說   ,在將對象存入其中時,通過被存入對象的hashCode() 來確定對象在HashSet中的存儲地址,通過equals()來確定存入的對象是否重複,hashCode() ,equals()都需要自己重新定義,因爲hashCode()默認前面已經說啦,而equals()   默認是比較的對象引用,你現在想一下,如果你不定義equals()的話,那麼同一個類產生的兩個內容完全相同的對象都可以存入Set,因爲他們是通過equals()來確定的,這樣就使得HashSet失去了他的意義,看一下下面這個:
import java.util.*;

public class Test {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        for (int i = 0; i <= 3; i++){
            set.add(new Demo1(i,i));           
        }
        System.out.println(set);
        set.add(new Demo1(1,1));
        System.out.println(set);
        System.out.println(set.contains(new Demo1(0,0)));
        System.out.println(set.add(new Demo1(1,1)));
        System.out.println(set.add(new Demo1(4,4)));
        System.out.println(set);
    }

    private static class Demo1 {
        private int value;
       
        private int id;

        public Demo1(int value,int id) {
            this.value = value;
            this.id=id;
        }

        public String toString() {
            return " value = " + value;
        }

        public boolean equals(Object o) {
            Demo1 a = (Demo1) o;
            return (a.value == value) ? true : false;
        }

        public int hashCode() {
            return id;
        }
    }
}
你分別註釋掉hashCode()和   equals()來比較一下他們作用就可以拉,關鍵要自己動手看看比較的結果你就可以記得很清楚啦

如果還不是很明確可以再看另一個例子:
import java.util.HashMap;
import java.util.Map;


public final class Test {

    public static void main(String[] args) {
        Map m = new HashMap();
        m.put(new PhoneNumber(020, 12345678), "shellfeng");
        System.out.println(m.get(new PhoneNumber(020, 12345678)));
    }

    private static class PhoneNumber {
       
        private short areaCode;

       
        private short extension;

        public PhoneNumber(int areaCode, int extension) {
            this.areaCode = (short) areaCode;
            this.extension = (short) extension;
        }

        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }
            if (!(o instanceof PhoneNumber)) {
                return false;
            }
            PhoneNumber pn = (PhoneNumber) o;
            return pn.extension == extension && pn.areaCode == areaCode;
        }

       
        public int hashCode() {
            int result = 17;
            result = 37 * result + areaCode;
            result = 37 * result + extension;
            return result;
        }
    }
}

還是那句話:你註釋掉hashCode()比較一下他們作用就可以拉,關鍵要自己動手看看比較的結果你就可以記得很清楚啦

總結
hashCode()方法使用來提高Map裏面的搜索效率的,Map會根據不同的hashCode()來放在不同的桶裏面,Map在搜索一個對象的時候先通過hashCode()找到相應的桶,然後再根據equals()方法找到相應的對象.要正確的實現Map裏面查找元素必須滿足一下兩個條件:
(1)當obj1.equals(obj2)爲true時obj1.hashCode()   ==   obj2.hashCode()必須爲true
(2)當obj1.hashCode() == obj2.hashCode()爲false時obj.equals(obj2)必須爲false

Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。
那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?這就是Object.equals方法了。
但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。
也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。
哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。我們可以認爲hashCode方法返回的就是對象存儲的物理地址(實際可能並不是,例如:通過獲取對象的物理地址然後除以8再求餘,餘數幾是計算得到的散列值,我們就認爲返回一個不是物理地址的數值,而是一個可以映射到物理地址的值)。
這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。所以這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。


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