散列函數 一個把查找表中的關鍵字映射成該關鍵字對應的地址的函數,記作Hash(key)=Addr(這裏的地址可能爲數組下標、索引、或者內存地址)
衝突 散列表可能把兩個或者兩個以上的不一樣關鍵字映射到同一地址,這種狀況成爲衝突
散列表 根據關鍵字直接進行訪問的數據結構(散列表創建了關鍵字與存儲地址之間的映射關係)html
1>散列表定義域需包含所有須要存儲的關鍵字
2>散列函數計算的地址應該等機率、均勻的分佈整個地址空間
3>散列函數應儘可能簡單,在較短期內計算出任意關鍵字對應的散列地址java
常見的幾種散列函數:
1 直接地址法
直接取關鍵字的某個線性函數值爲散列地址,散列函數爲:
H(key) = a*key+b
這種計算方法簡單,而且不會產生衝突,適合關鍵字的分佈基本連續,若關鍵字不連續,容易形成較多空位,形成存儲空間的浪費。算法
2除留餘數法
假設散列表表長爲m,取一個不大於m但接近或等於m的質數p,利用一下公式
H(key)=key%p
除留餘數法要選好p,使得每一個關鍵字經過該函數轉換後等機率映射到散列空間的任意地址。數組
3數字分析法
4平方取中法
5摺疊法數據結構
下面談下java中的hashcode()函數
總結幾個關鍵點:this
一、HashCode的存在主要是爲了查找的快捷性,HashCode是用來在散列存儲結構中肯定對象的存儲地址的code
二、若是兩個對象equals相等,那麼這兩個對象的HashCode必定也相同htm
三、若是對象的equals方法被重寫,那麼對象的HashCode方法也儘可能重寫對象
四、若是兩個對象的HashCode相同,不表明兩個對象就相同,只能說明這兩個對象在散列存儲結構中,存放於同一個位置
例子:
1 假設內存中有8個位置,一、二、三、四、五、六、七、8,將數據A 存入8個位置中,若是隨機存放,查找就從頭遍歷,
2 若是使用HashCode則效率會快不少,把A的HashCode%8,而後把ID存放在取得餘數的那個位置
3 若是A的 HashCode%8算出來的位置上自己已經有數據了。這就取決於算法的實現了,好比ThreadLocal中的作法就是從算出來的位置向後查找第一個爲空的位置,放置數據;HashMap的作法就是經過鏈式結構連起來。反正,只要保證放的時候和取的時候的算法一致就好了。
四、若是ID的 HashCode%8相等怎麼辦(這種對應的是第三點說的鏈式結構的場景)?這時候就須要定義equals了。先經過HashCode%8來判斷類在哪一 個位置,再經過equals來在這個位置上尋找須要的類。對比兩個類的時候也差很少,先經過HashCode比較,假如HashCode相等再判斷 equals。若是兩個類的HashCode都不相同,那麼這兩個類一定是不一樣的。
還有一個是set的例子
咱們都知道List 存放的元素是有序能夠重複的,而Set存放的是無序且不重複的
可是Set怎樣實現不重複呢,若是Set中數據不少,則調用equals方法效率很低,可是HashSet底層基於HashCode,咱們向集合中添加一個元素,
首先調用Hashcode方法,定位到一個位置,若是該位置爲空,則直接插入。反之注意比較,先比較Hashcode是否相同,相同的話不存。Hashcode不一樣則經過equals方法比較
若是不一樣則插入該元素。
再談重寫Object的equals(Object obj)方法爲何儘可能要重寫Object的hashCode()方法
首先
public class HashCodeClass { private String str0; private double dou0; private int int0; public boolean equals(Object obj) { if (obj instanceof HashCodeClass) { HashCodeClass hcc = (HashCodeClass)obj; if (hcc.str0.equals(this.str0) && hcc.dou0 == this.dou0 && hcc.int0 == this.int0) { return true; } return false; } return false; } }
打印出複寫equals方法後的該類的hashcode
public class TestMain { public static void main(String[] args) { System.out.println(new HashCodeClass().hashCode()); System.out.println(new HashCodeClass().hashCode()); System.out.println(new HashCodeClass().hashCode()); System.out.println(new HashCodeClass().hashCode()); System.out.println(new HashCodeClass().hashCode()); System.out.println(new HashCodeClass().hashCode()); } }
值爲
1901116749 1807500377 355165777 1414159026 1569228633 778966024
如上述代碼沒有複寫hashCode值,那麼將幾個類存入Set中因爲hashCode不相同,則set中存入多個HashCodeClass,這樣不合理,因此要複寫Hashcode()
Integer 複寫Hashcode()
public int hashCode() { return value; } public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
String複寫Hashcode()
public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h; } public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }
entity 複寫Hashode()
public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; }