算法6-1:哈希函数
在上章節中已經介紹了通過紅黑樹實現鍵值對數組的查詢操作,復雜度是logN。
有沒有性能更好的算法呢?答案是有。
基本想法就是計算keyword的哈希值,再通過哈希值直接獲取相應的鍵值。
這樣的方法的須要解決的問題是:
-
怎樣計算哈希值
-
怎樣解決哈系沖突
哈希函數
目標
依據對象中的成員變量的值,依照一定的規則計算出一個整數。這個整數就是哈希值。
哈希值最重要的兩個屬性是:
-
假設a.equals(b),那么a.hashCode() == b.hashCode()
-
理想狀況下。假設!a.equals(b),那么a.hashCode() != b.hashCode()
Java中的hash
Java中的Object對象中已經包括了hashCode函數,因為全部的對象都繼承自Object,因此全部的對象都有hashCode函數。該函數能返回一個整數。代表這個實例的哈希值。
Java中Integer類型的hashCode代碼例如以下:
public int hashCode() {return value; }
Double類型的hashCode代碼例如以下:
String類型的hashCode代碼例如以下:
public int hashCode() {int off = offset;char val[] = value;int len = count;int h = 0;for(int i = 0; i < len; i++) {h = 31*h + val[off++];}return h; }這樣的計算哈系的辦法稱之為Hornor哈希法。這樣的方法是一種很簡單的哈系算法。構造哈系沖突是很easy的。在2011年11月,有人發現Java的HashMap存在漏洞easy讓黑客實現Dos攻擊,它的原理就是構造大量的哈系沖突讓HashMap的復雜度從1變為N,占用大量的CPU資源,BUG的具體信息戳這里:https://bugzilla.redhat.com/show_bug.cgi?
id=CVE-2012-2739
因為String是不可變的類型,因此能夠對hashCode進行緩存。
自己定義類型的hash計算
public class Student {private int number;private String name;private String classname;public int hashCode() {int hash = 17;hash = hash*31 + name.hashCode();classname = hash*31 + classname.hashCode();} }
其原理就是依照Hornor哈系法將各個成員變量的哈希值連接在一起。
哈希的取模操作
取模操作就是希望讓哈系值能在0 ~ M-1范圍內,便于通過它來訪問數組。
第一種方法的代碼例如以下:
private int hash(Key key) {return key.hashCode() % M; }
這段代碼是錯的。
這樣的方法使用了取余數的操作,對于負數就會產生錯誤。
另外一種方法的代碼例如以下:
private int hash(Key key) {return Math.abs(key.hashCode()) % M; }
這段代碼中有BUG。這樣的方法在hashCode為負的0x80000000時會錯誤發生。由于它不能取相反數。
第三種方法的代碼例如以下:
private int hash(Key key) {return (key.hashCode() & 0x7fffffff) % M; }
這樣的方法才是正確的。
總結
以上是生活随笔為你收集整理的算法6-1:哈希函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 邮储银行APP如何修改登陆帐号
- 下一篇: 蘑菇街app是做什么的