哈希(Hash):也称散列,是一种时间换空间的算法思想。

1、哈希

哈希(Hash):也称散列,是一种时间换空间的算法思想。

哈希函数(哈希算法):设置一定的计算规则,将任意长度的输入,变换成固定长度的输出。

  1. 关键码值:元素中能起标识作用的数据,作为哈希函数的输入。
  2. 哈希值:哈希函数的输出。
关键码值(key)  -->  【哈希函数】  -->  哈希值(hash)

1.1、哈希表

哈希表(Hash Table):一种数据结构。

基于哈希函数建立的表。

  1. 映射过程:基于关键码值(key)计算出哈希值(hash),映射到表中的某个位置。
  2. 映射位置:有两种方案。
  1. hash 直接作为存放位置。
  2. 基于 hash 计算存放位置。

1.2、哈希冲突

哈希冲突:也称哈希碰撞。

指根据不同的输入,计算出了相同的哈希值。

常见解决方法

  1. 开放寻址法:基于产生冲突的地址(H0),重新计算直到得出可用地址(Hi)。
  1. 计算公式Hi = (H0 + di) % m
  2. 寻址方式:根据 m 的不同取值。
  • 线性探测再散列:1,2,3,...,m-1
  • 平方探测再散列:1,2,-2,4,-4,...,k2,-k2
  • 随机探测再散列:伪随机数
  1. 再哈希法:构造多个哈希函数,产生冲突时使用另一个函数计算,直到得出可用地址。
  2. 链地址法(拉链法):在产生冲突的位置上,形成链表。
  3. 建立公共溢出区:将哈希表分为基本表和溢出表,凡是冲突的元素就放入溢出表。

2、Java 哈希

2.1、集合

2.1.1、存储结构

(参考 HashMap 源码)哈希表 = 数组 + 位桶

  1. 结点类:哈希表中的基本元素,称为 bucket(位桶)。
  1. 哈希值
  2. 关键码值、元素值
  3. 后继结点
  1. 数组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+ 还引入了红黑树)

java新建一个哈希表 java实现哈希算法_数组

2.2、检索机制

哈希集合中检索元素遵守的机制(包括增删改查)

2.2.1、涉及方法

👉 浅谈 equals() 和 hashCode()

  1. hashCode():计算对象哈希值,用于确定元素存储位置。
  2. equals():比较对象是否相等,用于正确检索数据。

2.2.2、流程示例

插入元素 x 的大致流程

(代码已简化处理,具体请看源码)

  1. 扰动函数:将对象的 hashCode() 进一步处理。
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  1. 计算存储位置(n - 1) & hash,等价于 hash % n
Node<K,V>[] tab = table;	// 存储元素的哈希表
int n = tab.size;	// 当前元素个数

int i = (n - 1) & hash;		// 存储位置
  1. 判断待插入位置是否为空。
  1. :将元素放到数组下标处。
tab[i] = newNode(hash, key, value, null);
  1. :遍历链表,逐个元素比较是否相等(hash 和 equals() 都相等)。
  1. 发现相等:说明已存在相同 key 的记录,不会重复添加。
  2. 遍历到链尾:说明不存在重复记录,完成元素插入操作。