Normalerweise ist der Eingaberaum einer Hash-Funktion viel größer als der Ausgaberaum, sodass Hash-Konflikte theoretisch unvermeidlich sind. Wenn der Eingaberaum beispielsweise ausschließlich aus Ganzzahlen besteht und der Ausgaberaum aus der Array-Kapazität besteht, müssen mehrere Ganzzahlen demselben Array-Index zugeordnet sein.
Zu den gängigen Methoden zur Lösung von Hash-Konflikten gehören die Kettenadressmethode und die offene Adressierungsmethode.
Hinweis:Ob es sich um eine offene Adressierung oder eine Kettenadressmethode handelt,sie können nur den Hash garantieren Die Tabelle kann bei Kollisionen normal funktionieren, kann jedoch das Auftreten von Hash-Kollisionen nicht reduzieren. Gängige Hashing-Algorithmen sind: DM5, sha-1, sha-2 und sha-3.
In der ursprünglichen Hash-Tabelle kann jeder Bucket nur ein Schlüssel-Wert-Paar speichern. „Separate Verkettungsadresse“ wandelt ein einzelnes Element in eine verknüpfte Liste um, verwendet Schlüssel-Wert-Paare als verknüpfte Listenknoten und speichert alle widersprüchlichen Schlüssel-Wert-Paare in derselben verknüpften Liste.
package com.wei.mybatisflex.demo;
import java.util.ArrayList;
import java.util.List;
/**
* 链式地址哈希表
*/
public class HashMapChaining {
int size; // 键值对数量
int capacity; // 哈希表容量
double loadThres; // 触发扩容的负载因子阈值=(键值对数量/哈希表容量) 标准为: 0.75
int extendRatio; // 扩容倍数
List<List<Pair>> buckets; // 桶数组,这里用动态数组List<Pair>代替链表,一个桶就是一个链表。
/**
* 键值对定义
*/
class Pair{
private int key;
private String val;
public Pair(int key, String val) {
this.key = key;
this.val = val;
}
}
/* 构造方法 */
public HashMapChaining() {
size = 0;
capacity = 4;
loadThres = 2 / 3.0;
extendRatio = 2;
buckets = new ArrayList<>(capacity);
for (int i = 0; i < capacity; i++) {
buckets.add(new ArrayList<>());
}
}
/* 哈希函数 */
int hashFunc(int key) {
return key % capacity;
}
/* 负载因子 */
double loadFactor() {
return (double) size / capacity;
}
/* 查询操作 */
String get(int key) {
int index = hashFunc(key);
List<Pair> bucket = buckets.get(index);
// 遍历桶,若找到 key 则返回对应 val
for (Pair pair : bucket) {
if (pair.key == key) {
return pair.val;
}
}
// 若未找到 key 则返回 null
return null;
}
/* 添加操作 */
void put(int key, String val) {
// 当负载因子超过阈值时,执行扩容
if (loadFactor() > loadThres) {
extend();
}
int index = hashFunc(key);
List<Pair> bucket = buckets.get(index);
// 遍历桶,若遇到指定 key ,则更新对应 val 并返回
for (Pair pair : bucket) {
if (pair.key == key) {
pair.val = val;
return;
}
}
// 若无该 key ,则将键值对添加至尾部
Pair pair = new Pair(key, val);
bucket.add(pair);
size++;
}
/* 删除操作 */
void remove(int key) {
int index = hashFunc(key);
List<Pair> bucket = buckets.get(index);
// 遍历桶,从中删除键值对
for (Pair pair : bucket) {
if (pair.key == key) {
bucket.remove(pair);
size--;
break;
}
}
}
/* 扩容哈希表 */
void extend() {
// 暂存原哈希表
List<List<Pair>> bucketsTmp = buckets;
// 初始化扩容后的新哈希表
capacity *= extendRatio;
buckets = new ArrayList<>(capacity);
for (int i = 0; i < capacity; i++) {
buckets.add(new ArrayList<>());
}
size = 0;
// 将键值对从原哈希表搬运至新哈希表
for (List<Pair> bucket : bucketsTmp) {
for (Pair pair : bucket) {
put(pair.key, pair.val);
}
}
}
/* 打印哈希表 */
void print() {
for (List<Pair> bucket : buckets) {
List<String> res = new ArrayList<>();
for (Pair pair : bucket) {
res.add(pair.key + " -> " + pair.val);
}
System.out.println(res);
}
}
/**
* 测试
*/
public static void main(String[] args) {
HashMapChaining hashMapChaining = new HashMapChaining();
hashMapChaining.put(123, "23");
hashMapChaining.put(124, "24");
hashMapChaining.put(125, "25");
hashMapChaining.put(126, "26");
System.out.println("哈希表展示如下:");
hashMapChaining.print();
System.out.println("==================");
String s = hashMapChaining.get(123);
System.out.println("k=123对应的val值是:"+s);
System.out.println("==================");
hashMapChaining.put(124, "99");
System.out.println("把key=124的值换成99");
hashMapChaining.print();
System.out.println("==================");
hashMapChaining.remove(124);
System.out.println("删除key=124所对应的值:");
hashMapChaining.print();
}
}