哈希表(散列)的定義
散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
哈希表的特點是采用以常數平均時間執行插入、刪除和查找。
一個通俗的例子是,為了查找電話簿中某人的號碼,可以創建一個按照人名首字母順序排列的表(即建立人名
到首字母
的一個函數關系),在首字母為W的表中查找“王”姓的電話號碼,顯然比直接查找就要快得多。這里使用人名作為關鍵字,“取首字母”是這個例子中散列函數的函數法則
,存放首字母的表對應散列表。關鍵字和函數法則理論上可以任意確定。
分離鏈接法定義
將散列到同一個值得所有元素保留到一個表中。
基本思想是采用N個鏈表組成鏈表數組,N為哈希表的長度。

哈希表構造實現
1 public SeparateChainingHashTable() { 2 this(DEFAULT_TABLE_SIZE); 3 } 4 public SeparateChainingHashTable(int size) { 5 theLists=new LinkedList[nextPrime(size)]; 6 for(int i=0;i<theLists.length;i++) { 7 theLists[i]=new LinkedList<>();//初始化鏈表數組 8 } 9 }
基本操作實現
1 /* 2 * 哈希表插入元素 3 * */ 4 public void insert(T x) { 5 List<T> whichList=theLists[myhash(x)]; 6 /* 7 * 如果當前哈希地址的鏈表不含有元素,則鏈表中添加該元素 8 * */ 9 if(!whichList.contains(x)) { 10 whichList.add(x); 11 if(++currentSize>theLists.length)//如果表長度不夠,則擴容 12 rehash(); 13 } 14 } 15 public void remove(T x) { 16 List<T> whichList=theLists[myhash(x)]; 17 if(whichList.contains(x)) { 18 whichList.remove(x); 19 currentSize--; 20 } 21 } 22 public boolean contains(T x) { 23 List<T> whilchList=theLists[myhash(x)]; 24 return whilchList.contains(x); 25 } 26 public void makeEmpty() { 27 for(int i=0;i<theLists.length;i++) 28 theLists[i].clear(); 29 currentSize=0; 30 }
哈希表相關實現
1 private void rehash() { 2 List<T>[] oldLists=theLists; 3 theLists=new List[nextPrime(2*theLists.length)]; 4 for(int j=0;j<theLists.length;j++) 5 theLists[j]=new LinkedList<>(); 6 7 currentSize=0; 8 /* 9 * 更新哈希表 10 * */ 11 for(List<T> list:oldLists) 12 for(T item:list) 13 insert(item); 14 } 15 /* 16 * myhash()方法獲得哈希表的地址 17 * */ 18 private int myhash(T x) { 19 int hashVal=x.hashCode();//hashCode()方法返回該對象的哈希碼值 20 hashVal%=theLists.length;//對哈希表長度取余數 21 if(hashVal<0) 22 hashVal+=theLists.length; 23 return hashVal; 24 }
全部代碼
1 import java.util.LinkedList; 2 import java.util.List; 3 4 public class SeparateChainingHashTable<T>{ 5 public SeparateChainingHashTable() { 6 this(DEFAULT_TABLE_SIZE); 7 } 8 public SeparateChainingHashTable(int size) { 9 theLists=new LinkedList[nextPrime(size)]; 10 for(int i=0;i<theLists.length;i++) { 11 theLists[i]=new LinkedList<>();//初始化鏈表數組 12 } 13 } 14 15 /* 16 * 哈希表插入元素 17 * */ 18 public void insert(T x) { 19 List<T> whichList=theLists[myhash(x)]; 20 /* 21 * 如果當前哈希地址的鏈表不含有元素,則鏈表中添加該元素 22 * */ 23 if(!whichList.contains(x)) { 24 whichList.add(x); 25 if(++currentSize>theLists.length)//如果表長度不夠,則擴容 26 rehash(); 27 } 28 } 29 public void remove(T x) { 30 List<T> whichList=theLists[myhash(x)]; 31 if(whichList.contains(x)) { 32 whichList.remove(x); 33 currentSize--; 34 } 35 } 36 public boolean contains(T x) { 37 List<T> whilchList=theLists[myhash(x)]; 38 return whilchList.contains(x); 39 } 40 public void makeEmpty() { 41 for(int i=0;i<theLists.length;i++) 42 theLists[i].clear(); 43 currentSize=0; 44 } 45 46 private static final int DEFAULT_TABLE_SIZE=101; 47 48 private List<T> [] theLists; 49 private int currentSize; 50 51 /* 52 * 哈希表擴容,表長度為下一個素數 53 * */ 54 private void rehash() { 55 List<T>[] oldLists=theLists; 56 theLists=new List[nextPrime(2*theLists.length)]; 57 for(int j=0;j<theLists.length;j++) 58 theLists[j]=new LinkedList<>(); 59 60 currentSize=0; 61 /* 62 * 更新哈希表 63 * */ 64 for(List<T> list:oldLists) 65 for(T item:list) 66 insert(item); 67 } 68 /* 69 * myhash()方法獲得哈希表的地址 70 * */ 71 private int myhash(T x) { 72 int hashVal=x.hashCode();//hashCode()方法返回該對象的哈希碼值 73 hashVal%=theLists.length;//對哈希表長度取余數 74 if(hashVal<0) 75 hashVal+=theLists.length; 76 return hashVal; 77 } 78 //下一個素數 79 private static int nextPrime(int n) { 80 if( n % 2 == 0 ) 81 n++; 82 83 for( ; !isPrime( n ); n += 2 ) 84 ; 85 86 return n; 87 } 88 //判斷是否是素數 89 private static boolean isPrime(int n) { 90 if( n == 2 || n == 3 ) 91 return true; 92 93 if( n == 1 || n % 2 == 0 ) 94 return false; 95 96 for( int i = 3; i * i <= n; i += 2 ) 97 if( n % i == 0 ) 98 return false; 99 100 return true; 101 } 102 }

到首字母
的一個函數關系),在首字母為W的表中查找“王”姓的電話號碼,顯然比直接查找就要快得多。這里使用人名作為關鍵字,“取首字母”是這個例子中散列函數的函數法則
,存放首字母的表對應散列表。關鍵字和函數法則理論上可以任意確定。