1. 程式人生 > >Java面試——HashCode的作用原理和例項解析

Java面試——HashCode的作用原理和例項解析

雜湊碼(hash code)是由物件匯出的一個整型值,雜湊碼是沒有規律的。如果x和y是兩個不同的物件,x.hashCode()與y.hashCode()基本上不會相同。

由於hashCode方法定義在Object類中,因此每個物件都有一個預設的雜湊碼,其值是物件的儲存地址。

1.  HashCode的特性

1HashCode的存在主要是用於查詢的快捷性,如HashtableHashMap等,HashCode經常用於確定物件的儲存地址

2如果兩個物件相同 equals方法一定返回true並且這兩個物件的HashCode一定相同;

3兩個物件的HashCode相同,並不一定表示兩個物件就相同

,即equals()不一定為true,只能夠說明這兩個物件在一個雜湊儲存結構中。

4)如果物件的equals方法被重寫,那麼物件的HashCode也儘量重寫。

2.  HashCode作用

Java中的集合有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複

equals方法可用於保證元素不重複,但如果每增加一個元素就檢查一次,若集合中現在已經有1000個元素,那麼第1001個元素加入集合時,就要呼叫1000次equals方法。這顯然會大大降低效率。 於是,Java採用了雜湊表的原理

雜湊演算法也稱為雜湊演算法,是將資料依特定演算法直接指定到一個地址上。

這樣一來,當集合

要新增新的元素時,先呼叫這個元素的HashCode方法,就一下子能定位到它應該放置的物理位置上。

1)如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;

2)如果這個位置上已經有元素了,就呼叫它的equals方法與新元素進行比較,相同的話就不存了;

3不相同的話,也就是發生了Hash key相同導致衝突的情況,那麼就在這個Hash key的地方產生一個連結串列,將所有產生相同HashCode的物件放到這個單鏈表上去,串在一起(很少出現)。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。

如何理解HashCode的作用:

Object角度看,JVM

new一個Object,它都會將這個Object丟到一個Hash表中去,這樣的話,下次做Object比較或者取這個物件的時候(讀取過程),它會根據物件的HashCode再從Hash表中取這個物件。這樣做的目的是提高取物件的效率。若HashCode相同再去呼叫equal

3.  HashCode實踐

HashCode是用於查詢使用的,而equals是用於比較兩個物件是否相等的。

1)例如記憶體中有這樣的位置

0  1  2  3  4  5  6  7    
而我有個類,這個類有個欄位叫ID,我要把這個類存放在以上8個位置之一,如果不用HashCode而任意存放,那麼當查詢時就需要到這八個位置裡挨個去找,或者用二分法一類的演算法。

但以上問題如果用HashCode就會使效率提高很多定義我們的HashCodeID8,比如我們的ID998的餘數為1,那麼我們就把該類存在1這個位置,如果ID13,求得的餘數是5,那麼我們就把該類放在5這個位置。依此類推。

2)但是如果兩個類有相同的HashCode,例如9除以817除以8的餘數都是1也就是說,我們先通過 HashCode來判斷兩個類是否存放某個桶裡,但這個桶裡可能有很多類,那麼我們就需要再通過equals在這個桶裡找到我們要的類。

請看下面這個例子 :

public class HashTest {  
    private int i;  
  
    public int getI() {  
        return i;  
    }  
  
    public void setI(int i) {  
        this.i = i;  
    }  
  
    public int hashCode() {  
        return i % 10;  
    }  
  
    public final static void main(String[] args) {  
        HashTest a = new HashTest();  
        HashTest b = new HashTest();  
        a.setI(1);  
        b.setI(1);  
        Set<HashTest> set = new HashSet<HashTest>();  
        set.add(a);  
        set.add(b);  
        System.out.println(a.hashCode() == b.hashCode());  
        System.out.println(a.equals(b));  
        System.out.println(set);  
    }  
}  

輸出結果為:

true
false
[[email protected], [email protected]]

以上這個示例,我們只是重寫了HashCode方法,從上面的結果可以看出,雖然兩個物件的HashCode相等,但是實際上兩個物件並不是相等,因為我們沒有重寫equals方法,那麼就會呼叫Object預設的equals方法,顯示這是兩個不同的物件。

這裡我們將生成的物件放到了HashSet中,而HashSet只能夠存放唯一的物件,也就是相同的(適用於equals方法)的物件只會存放一個,但是這裡實際上是兩個物件ab都被放到了HashSet中,這樣HashSet就失去了他本身的意義了。

下面我們繼續重寫equals方法:


public class HashTest {  
    private int i;  
  
    public int getI() {  
        return i;  
    }  
  
    public void setI(int i) {  
        this.i = i;  
    }  
  
    public boolean equals(Object object) {  
        if (object == null) {  
            return false;  
        }  
        if (object == this) {  
            return true;  
        }  
        if (!(object instanceof HashTest)) {  
            return false;  
        }  
        HashTest other = (HashTest) object;  
        if (other.getI() == this.getI()) {  
            return true;  
        }  
        return false;  
    }
  
    public int hashCode() {  
        return i % 10;  
    }  
  
    public final static void main(String[] args) {  
        HashTest a = new HashTest();  
        HashTest b = new HashTest();  
        a.setI(1);  
        b.setI(1);  
        Set<HashTest> set = new HashSet<HashTest>();  
        set.add(a);  
        set.add(b);  
        System.out.println(a.hashCode() == b.hashCode());  
        System.out.println(a.equals(b));  
        System.out.println(set);  
    }  
}  
輸出結果如下所示。

從結果我們可以看出,現在兩個物件就完全相等了,HashSet中也只存放了一份物件。

true
true
[[email protected]]