2025-11-27-JavaScript 实现哈希表

1491 个字
7 分钟
2025-11-27-JavaScript 实现哈希表

JavaScript 实现哈希表#

哈希表(Hash Table,散列表)是一种通过键(Key)直接访问值(Value)的数据结构,通过哈希函数将键映射到表中的位置

哈希函数#

// 简单的哈希函数示例
function hashString(key, tableSize) {
let hash = 17;
for (let i = 0; i < key.length; i++) {
hash = (13 * hash * key.charCodeAt(i)) % tableSize;
}
return hash;
}

用 JavaScript 实现哈希表#

class HashTable {
constructor(size = 53) {
this.keyMap = new Array(size);
}
// 哈希函数
_hash(key) {
let total = 0;
const PRIME = 31;
const MAX_LENGTH = 100;
for (let i = 0; i < Math.min(key.length, MAX_LENGTH); i++) {
const char = key[i];
const value = char.charCodeAt(0) - 96; // a=1, b=2...
total = (total * PRIME + value) % this.keyMap.length;
}
return total;
}
// 插入键值对
set(key, value) {
const index = this._hash(key);
if (!this.keyMap[index]) {
this.keyMap[index] = [];
}
// 检查键是否已存在,存在则更新
for (let i = 0; i < this.keyMap[index].length; i++) {
if (this.keyMap[index][i][0] === key) {
this.keyMap[index][i][1] = value;
return;
}
}
this.keyMap[index].push([key, value]);
}
// 获取值
get(key) {
const index = this._hash(key);
if (this.keyMap[index]) {
for (let i = 0; i < this.keyMap[index].length; i++) {
if (this.keyMap[index][i][0] === key) {
return this.keyMap[index][i][1];
}
}
}
return undefined;
}
// 删除键值对
delete(key) {
const index = this._hash(key);
if (!this.keyMap[index]) return false;
for (let i = 0; i < this.keyMap[index].length; i++) {
if (this.keyMap[index][i][0] === key) {
this.keyMap[index].splice(i, 1);
if (this.keyMap[index].length === 0) {
delete this.keyMap[index];
}
return true;
}
}
return false;
}
// 获取所有键
keys() {
const keysArr = [];
for (let i = 0; i < this.keyMap.length; i++) {
if (this.keyMap[i]) {
for (let j = 0; j < this.keyMap[i].length; j++) {
if (!keysArr.includes(this.keyMap[i][j][0])) {
keysArr.push(this.keyMap[i][j][0]);
}
}
}
}
return keysArr;
}
// 获取所有值
values() {
const valuesArr = [];
for (let i = 0; i < this.keyMap.length; i++) {
if (this.keyMap[i]) {
for (let j = 0; j < this.keyMap[i].length; j++) {
if (!valuesArr.includes(this.keyMap[i][j][1])) {
valuesArr.push(this.keyMap[i][j][1]);
}
}
}
}
return valuesArr;
}
}

自动扩容#

class EnhancedHashTable {
constructor(initialCapacity = 8, loadFactor = 0.75) {
this.capacity = initialCapacity;
this.loadFactor = loadFactor;
this.size = 0;
this.buckets = new Array(this.capacity);
}
_hash(key) {
let hashCode = 0;
const PRIME = 31;
// 处理不同类型的键
const keyStr = typeof key === "string" ? key : JSON.stringify(key);
for (let i = 0; i < keyStr.length; i++) {
hashCode = (PRIME * hashCode + keyStr.charCodeAt(i)) % this.capacity;
}
return hashCode;
}
_resize() {
const oldBuckets = this.buckets;
this.capacity *= 2;
this.buckets = new Array(this.capacity);
this.size = 0;
for (const bucket of oldBuckets) {
if (bucket) {
for (const [key, value] of bucket) {
this.set(key, value);
}
}
}
}
set(key, value) {
// 检查是否需要扩容
if (this.size / this.capacity >= this.loadFactor) {
this._resize();
}
const index = this._hash(key);
if (!this.buckets[index]) {
this.buckets[index] = [];
}
// 更新或添加
for (let i = 0; i < this.buckets[index].length; i++) {
if (this.buckets[index][i][0] === key) {
this.buckets[index][i][1] = value;
return;
}
}
this.buckets[index].push([key, value]);
this.size++;
}
get(key) {
const index = this._hash(key);
const bucket = this.buckets[index];
if (bucket) {
for (const [k, v] of bucket) {
if (k === key) return v;
}
}
return undefined;
}
has(key) {
return this.get(key) !== undefined;
}
clear() {
this.buckets = new Array(this.capacity);
this.size = 0;
}
// 获取负载因子
getLoadFactor() {
return this.size / this.capacity;
}
}

支持任何类型键的通用哈希表#

class UniversalHashTable {
constructor(size = 53) {
this.table = new Array(size);
}
// 通用哈希函数,支持多种类型
_hash(key) {
if (typeof key === "number") {
return key % this.table.length;
}
if (typeof key === "string") {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash; // 转为32位整数
}
return Math.abs(hash) % this.table.length;
}
if (typeof key === "object" && key !== null) {
// 对象使用JSON字符串化
return this._hash(JSON.stringify(key));
}
return 0;
}
// 双重哈希解决冲突
_hash2(key) {
return 7 - (this._hash(key) % 7);
}
// 使用线性探测开放寻址
_findSlot(key, forInsert = false) {
let index = this._hash(key);
let step = 1;
while (this.table[index] !== undefined) {
if (this.table[index] !== null && this.table[index].key === key) {
return index; // 找到键
}
if (
forInsert &&
(this.table[index] === null || this.table[index] === undefined)
) {
return index; // 找到可插入的空槽
}
// 线性探测
index = (index + 1) % this.table.length;
step++;
if (step > this.table.length) {
throw new Error("Hash table is full");
}
}
return forInsert ? index : -1;
}
set(key, value) {
const index = this._findSlot(key, true);
this.table[index] = { key, value };
}
get(key) {
const index = this._findSlot(key, false);
return index !== -1 ? this.table[index].value : undefined;
}
delete(key) {
const index = this._findSlot(key, false);
if (index !== -1) {
this.table[index] = null; // 标记为删除
return true;
}
return false;
}
}

使用 JavaScript 内置结构#

使用 Object

// 最简单的哈希表实现
const hashTable = {};
hashTable["key1"] = "value1";
hashTable["key2"] = "value2";
// 获取
const value = hashTable["key1"];
// 删除
delete hashTable["key1"];

使用 Map

// ES6 Map 是更好的哈希表实现
const map = new Map();
// 设置键值对
map.set("name", "Alice");
map.set(42, "The Answer");
map.set({ id: 1 }, "Object Key");
// 获取
console.log(map.get("name")); // Alice
// 检查是否存在
console.log(map.has(42)); // true
// 删除
map.delete(42);
// 大小
console.log(map.size);
// 遍历
map.forEach((value, key) => {
console.log(key, value);
});
// 清空
map.clear();

使用 Set(类似哈希集合)

// 用于存储唯一值
const set = new Set();
set.add(1);
set.add(2);
set.add(2); // 重复,不会被添加
console.log(set.has(1)); // true
console.log(set.size); // 2
set.delete(1);

性能优化#

选择合适的哈希函数

// 更好的字符串哈希函数(djb2算法)
function hashDJB2(str, tableSize) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = (hash * 33) ^ str.charCodeAt(i);
}
return Math.abs(hash) % tableSize;
}

优化冲突处理

class OptimizedHashTable {
constructor(size = 53) {
this.table = new Array(size);
this.deleted = Symbol("deleted"); // 特殊标记删除
}
// 二次探测
_probe(index, i, tableSize) {
return (index + i * i) % tableSize;
}
// 双重哈希
_doubleHash(index, i, tableSize, key) {
const hash2 = 1 + (this._hash2(key) % (tableSize - 1));
return (index + i * hash2) % tableSize;
}
}

实际应用#

频率计数器

function frequencyCounter(arr) {
const frequency = new Map();
for (const item of arr) {
frequency.set(item, (frequency.get(item) || 0) + 1);
}
return frequency;
}
// 使用
const arr = ["apple", "banana", "apple", "orange", "banana", "banana"];
const freq = frequencyCounter(arr);
console.log(freq.get("banana")); // 3

缓存实现(LRU Cache)

class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map(); // 哈希表 + 维护顺序
}
get(key) {
if (!this.cache.has(key)) return -1;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value); // 更新为最近使用
return value;
}
put(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 删除最久未使用的
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
}
}

分组算法

function groupBy(array, keyFn) {
const groups = new Map();
for (const item of array) {
const key = typeof keyFn === "function" ? keyFn(item) : item[keyFn];
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(item);
}
return groups;
}
// 使用
const people = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 25 },
];
const groupedByAge = groupBy(people, "age");
console.log(groupedByAge.get(25));
// [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }]

复杂度#

操作ObjectMap自定义哈希表
插入O(1)O(1)O(1)-O(n)
查找O(1)O(1)O(1)-O(n)
删除O(1)O(1)O(1)-O(n)
遍历键O(n)O(n)O(n)

最坏情况(所有键冲突)会退化为 O(n)

分享到社交平台

将本文分享给你的朋友们

2025-11-27-JavaScript 实现哈希表
https://firefly.cuteleaf.cn/posts/2025-12-25-关于javascript-实现哈希表/
作者
Zhongye
发布于
2025-11-27
版权声明
CC BY-NC-SA 4.0

评论

Profile Image of the Author
Zhongye
南漂中
公告
新的博客站!旧站点传送门 zhongye1.github.io/Arknight-notes
音乐
专辑封面

音乐

暂无播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章数
142
分类数
14
标签数
214
总字数
339,690
运行天数
0
最后更新
0 天前

目录