哈希及 Java 实现
1、哈希
哈希(Hash):也称散列,是一种时间换空间的算法思想。
哈希函数(哈希算法):设置一定的计算规则,将任意长度的输入,变换成固定长度的输出。
-
关键码值:元素中能起标识作用的数据,作为哈希函数的输入。
-
哈希值:哈希函数的输出。
关键码值(key) --> 【哈希函数】 --> 哈希值(hash)
1.1、哈希表
哈希表(Hash Table):一种数据结构。
基于哈希函数建立的表。
- 映射过程:基于关键码值(key)计算出哈希值(hash),映射到表中的某个位置。
- 映射位置:有两种方案。
- hash 直接作为存放位置。
- 基于 hash 计算存放位置。
1.2、哈希冲突
哈希冲突:也称哈希碰撞。
指根据不同的输入,计算出了相同的哈希值。
常见解决方法:
- 开放寻址法:基于产生冲突的地址(H0),重新计算直到得出可用地址(Hi)。
- 计算公式:
Hi = (H0 + di) % m
。 - 寻址方式:根据
m
的不同取值。- 线性探测再散列:1,2,3,...,m-1
- 平方探测再散列:1,2,-2,4,-4,...,k2,-k2
- 随机探测再散列:伪随机数
- 计算公式:
- 再哈希法:构造多个哈希函数,产生冲突时使用另一个函数计算,直到得出可用地址。
- 链地址法(拉链法):在产生冲突的位置上,形成链表。
- 建立公共溢出区:将哈希表分为基本表和溢出表,凡是冲突的元素就放入溢出表。
2、Java 哈希
2.1、集合
2.1.1、存储结构
(参考
HashMap
源码)哈希表 = 数组 + 位桶
-
结点类:哈希表中的基本元素,称为
bucket
(位桶)。- 哈希值
- 关键码值、元素值
- 后继结点
-
数组:
bucket
类型的数组,即哈希表。static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; } transient Node<K,V>[] table;
2.1.2、哈希冲突
Java 基于链地址法解决哈希冲突。
(Java 1.8+ 还引入了红黑树)
2.2、检索机制
哈希集合中检索元素遵守的机制(包括增删改查)
2.2.1、涉及方法
hashCode()
:计算对象哈希值,用于确定元素存储位置。equals()
:比较对象是否相等,用于正确检索数据。
2.2.2、流程示例
插入元素 x 的大致流程
(代码已简化处理,具体请看源码)
-
扰动函数:将对象的
hashCode()
进一步处理。static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
-
计算存储位置:
(n - 1) & hash
,等价于hash % n
。Node<K,V>[] tab = table; // 存储元素的哈希表 int n = tab.size; // 当前元素个数 int i = (n - 1) & hash; // 存储位置
-
判断待插入位置是否为空。
-
是:将元素放到数组下标处。
tab[i] = newNode(hash, key, value, null);
-
否:遍历链表,逐个元素比较是否相等(hash 和 equals() 都相等)。
- 发现相等:说明已存在相同 key 的记录,不会重复添加。
- 遍历到链尾:说明不存在重复记录,完成元素插入操作。
-
关于哈希在 Java 中的实现,
具体查看 👉 漫谈 HashMap