哈希表
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希函数
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
哈希表中元素是由哈希函数确定的。将数据元素的关键字K作为自变量,通过一定的函数关系(称为哈希函数),计算出的值,即为该元素的存储地址。
哈希冲突
由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,因此总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突。(两个不同的数据计算后的结果一样)
为了使哈希表的时间复杂度达到O(1)级别,哈希表需要具备容量的动态变化
HashTable.java(哈希表)
import java.util.TreeMap;
//哈希表
public class HashTable<K extends Comparable<K>, V> {// 由于底层是TreeMap,K要求可比较
private final static int[] capacity = { 13, 19, 29, 41, 59, 79, 107, 149, 197, 263, 347, 457, 599, 787, 1031, 1361,
1777, 2333, 3037, 3967, 5167, 6719, 8737, 11369, 14783, 19219, 24989, 32491, 42257, 54941, 71429, 92861,
120721, 156941, 204047, 265271, 344857, 448321, 582821, 757693, 985003, 1280519, 1664681, 2164111, 2813353,
3657361, 4754591, 6180989, 8035301, 10445899, 13579681, 17653589, 22949669, 29834603, 38784989, 50420551,
65546729, 85210757, 110774011, 144006217, 187208107, 243370577, 316381771, 411296309, 534685237, 695090819,
903618083, 1174703521, 1527114613, 1837299131, 2147483647 };// 素数表
private static final int upperTol = 10;// 扩容上界
private static final int lowerTol = 2;// 缩容下界
private int capacityIndex = 0;// 初始容量 为素数表索引
private TreeMap<K, V>[] hashtable;// hash表即一个存放TreeMap的数组
private int M;// hash表长度
private int size;// 存储元素个数
public HashTable() {// 素数进行开辟空间
// TODO Auto-generated constructor stub
this.M = capacity[capacityIndex];
size = 0;
hashtable = new TreeMap[M];
for (int i = 0; i < M; i++)
hashtable[i] = new TreeMap<>();
}
// 将key的值转换为当前的hash表中所对应的索引
private int hash(K key) {
return key.hashCode() & 0x7fffffff % M;// 去负号与对M取余
}
public int getSize() {
return size;
}
// 添加key
public void add(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];// 暂存key对应的TreeMap
if (map.containsKey(key))// 查看key对应的treemap中是否包含key
map.put(key, value);// 修改数据
else {
map.put(key, value);// 添加数据
size++;
// 扩容
if (size >= upperTol * M && capacityIndex + 1 < capacity.length) {// 等价size/M>=upperTol,避免除法中整型向浮点型转换;防止数组越界
capacityIndex++;
resize(capacity[capacityIndex]);
}
}
}
// 删除key并返回key对应的value
public V remove(K key) {
TreeMap<K, V> map = hashtable[hash(key)];// 暂存key对应的TreeMap
V ret = null;
if (map.containsKey(key)) {
ret = map.remove(key);
size--;
// 缩容
if (size < lowerTol * M && capacityIndex - 1 >= 0)// 缩容时避免容量过小
capacityIndex--;
resize(capacity[capacityIndex]);
}
return ret;
}
private void resize(int newM) {
// TODO Auto-generated method stub
TreeMap<K, V>[] newHashTable = new TreeMap[newM];
for (int i = 0; i < newM; i++)
newHashTable[i] = new TreeMap<>();
int oldM = M;// 存储旧的M
this.M = newM;// hash()中的M会变为newM
// 复制hashtable到newHashTable
for (int i = 0; i < oldM; i++) {
TreeMap<K, V> map = hashtable[i];
for (K key : map.keySet())
newHashTable[hash(key)].put(key, map.get(key));
}
this.hashtable = newHashTable;
}
// 修改key对应的value
public void set(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];// 暂存key对应的TreeMap
if (!map.containsKey(key))// map中不存在key抛出异常
throw new IllegalArgumentException();
map.put(key, value);
}
// 查询是否包含key
public boolean contains(K key) {
return hashtable[hash(key)].containsKey(key);
}
// 获取key所对应的value
public V get(K key) {
return hashtable[hash(key)].get(key);
}
}