哈希表(Java)

哈希表

散列表(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);
	}

}

总结

哈希表增删改查操作的均摊复杂度达到O(1)
哈希表牺牲了顺序性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值