class HashTable {
this.table = {};
}
toStrFn (key){
if (key === null) {
return ‘NULL‘;
} else if (key === undefined) {
return ‘UNDEFINED‘;
} else if (typeof key === ‘string‘ || key instanceof String) {
return `${key}`;
}else if ( Object.prototype.toString.call({})===‘[object Object‘] ){
return JSON.stringify(obj)
}
return key.toString();
}
loseloseHashCode(key) {
if (typeof key === ‘number‘) { // 检验key是否是一个数字
return key;
}
const tableKey = this.toStrFn(key); // 将 key 转换为一个字符串
let hash = 0; // 创建一个hash变量
for (let i = 0; i < tableKey.length; i++) { // 迭代转为字符串后的key
hash += tableKey.charCodeAt(i); // 从ASCII表中查到的每个字符对应的 ASCII 值加到 hash 变量中
}
return hash % 37; // 返回hash值。为了得到比较小的值,使用hash值和任意数取余(规避超过最大表示范围的风险,暂时有坑!!!)
}
hashCode(key) { //hashCode 方法简单地调用了 loseloseHashCode 方法,将 key 作为参数传入
return this.loseloseHashCode(key);
}
put(key, value) {
if (key != null && value != null) { // 检验 key 和 value 是否合法,如果不合法就返回 false
const position = this.hashCode(key); // 根据给出的key,在表中找到一个位置
this.table[position] = new ValuePair(key, value); // 用 key 和 value 创建一个 ValuePair (此实例和字典中的一样)实例
return true;
}
}
return false;
get(key) {
const valuePair = this.table[this.hashCode(key)];
return valuePair == null ? undefined : valuePair.value;
}
remove(key) {
const hash = this.hashCode(key); // 获取hash
const valuePair = this.table[hash]; // 获取值
if (valuePair != null) { // 如果有值
delete this.table[hash]; // 删除它
return true; // 返回true
}
return false; // 如果没找到对应的值,返回false
}
const hash = new HashTable();
hash.put(‘Gandalf‘, ‘gandalf@email.com‘);
hash.put(‘John‘, ‘johnsnow@email.com‘);
hash.put(‘Tyrion‘, ‘tyrion@email.com‘);
console.log(hash.hashCode(‘Gandalf‘) + ‘ - Gandalf‘); // 19 - Gandalf
console.log(hash.hashCode(‘John‘) + ‘ - John‘); // 29 - John
console.log(hash.hashCode(‘Tyrion‘)+‘ - Tyrion‘); // 16 - Tyrion
console.log(hash.get(‘Gandalf‘)); // gandalf@email.com
console.log(hash.get(‘Loiane‘)); // undefined 由于 Loiane 是一个不存在的键,所以返回会是 undefined(即不存在)。
hash.remove(‘Gandalf‘);
console.log(hash.get(‘Gandalf‘)); // undefined
const hash = new HashTable();
hash.put(‘Jonathan‘, ‘jonathan@email.com‘); 0
hash.put(‘Jamie‘, ‘jamie@email.com‘);
通过对每个提到的名字调用 hash.hashCode 方法,输出结果如下。
5 - Jonathan
5 - Jamie
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) { // 判断新元素的位置是否已被占据
this.table[position] = new LinkedList(); // 初始化一个 LinkedList 类(链表类的实现方法见链表传送门)的实例
}
this.table[position].push(new ValuePair(key, value)); // 向链表中添加一个ValuePair实例
return true;
}
return false;
}
get(key) {
const position = this.hashCode(key); // 转化hash值
const linkedList = this.table[position]; // 获取hash对应的地址
if (linkedList != null && !linkedList.isEmpty()) { // 如果链表实例存在
let current = linkedList.getHead(); // 如果有,获取链表头的引用地址
while (current != null) { // 迭代到最后
if (current.element.key === key) { // 找到key值与传入key相同的
return current.element.value; // 返回value值
}
current = current.next; // 如果key值与传入key不同,再往下找
}
}
return undefined; // 如果链表实例不存在,返回undefined
}
remove(key) {
const position = this.hashCode(key); // 转化hash值
const linkedList = this.table[position]; // 获取hash对应的地址
if (linkedList != null && !linkedList.isEmpty()) { // 如果链表实例存在
let current = linkedList.getHead(); // 如果有,获取链表头的引用地址
while (current != null) { // 迭代到最后
if (current.element.key === key) { // 找到key值与传入key相同的
linkedList.remove(current.element); // 使用 remove 方法将其从链表中移除
if (linkedList.isEmpty()) { // 删除后如果空了
delete this.table[position]; // 也要在散列表中的位置删除
}
return true; // 返回 true 表示该元素已经被移除
}
current = current.next; // 如果key值与传入key不同,再往下找
}
}
return false; // 返回false表示该元素在散列表中不存在
}
put(key, value) {
if (key != null && value != null) { // 检验传入的key和value是否有效
const position = this.hashCode(key); // 获取hash值
if (this.table[position] == null) { // 如果这个hash值的位置没有元素存在
this.table[position] = new ValuePair(key, value); // 直接等于一个ValuePair实例就好了
} else { // 反之,不存在
let index = position + 1; // 先创建一个变量,等于hash值加一
while (this.table[index] != null) { // 迭代,直到找到一个空位置
index++;
}
this.table[index] = new ValuePair(key, value); // 在这个空位置处放入一个ValuePair实例
}
return true;
}
return false;
}
get(key) { const position = this.hashCode(key);
if (this.table[position] != null) { // 确定这个键存在
if (this.table[position].key === key) { // 如果这个值没变动过
return this.table[position].value; // 直接返回该位置的value值
}
while (this.table[index] != null && this.table[index].key !== key) { // 如果这个值改变了,就从下一个位置继续迭代,直到找到要找的元素或者空位置
let index = position + 1;
index++;
}
if (this.table[index] != null && this.table[index].key === key) { //当跳出循环时,如果该位置不是空并且它的key和传入的key相同,返回它的value
return this.table[position].value;
}
return undefined; // {8}
}
}
verifyRemoveSideEffect(key, removedPosition) { // 该函数用于在删除后把添加时移动的值移回原位置,接收两个值:被删除的 key 和该 key 被删除的位置。
const hash = this.hashCode(key); // 获取被删除的 key 的 hash 值
let index = removedPosition + 1; // 创建一个变量,等于删除位置+1
while (this.table[index] != null) { // 迭代 直到找到空位置
const posHash = this.hashCode(this.table[index].key); // 迭代时当前位置上元素的 hash 值
if (posHash <= hash || posHash <= removedPosition) { // 如果当前元素的hash值小于等于原始的值或者小于等于删除key的hash值,就需要把它移动到删除的位置
this.table[removedPosition] = this.table[index];
delete this.table[index]; // 再删除它当前元素(因为它已经被复制到删除的位置了)
removedPosition = index; // 再把变量更新为新删除的位置,重复,直到有空位置
}
index++;
}
}
remove(key) {
const position = this.hashCode(key);
if (this.table[position] != null) { // 判断该位置是否有值
if (this.table[position].key === key) { // 如果该位置的key等于传入的key
delete this.table[position]; // 删除该值
this.verifyRemoveSideEffect(key, position); // 删除后把原来属于该位置的值挪回来
return true;
}
let index = position + 1; // 如果该位置的key不等于传入的key,证明被移动过
while (this.table[index] != null && this.table[index].key !== key ) { // 迭代
index++;
}
if (this.table[index] != null && this.table[index].key === key) {// 如果该位置不为空,并且它的key等于传入的key
delete this.table[index]; // 删除
this.verifyRemoveSideEffect(key, index); // 挪回来
return true;
}
}
return false;
}
djb2HashCode(key) {
const tableKey = this.toStrFn(key); // 先将键转化为字符串
let hash = 5381; // 初始化一个hash变量并复制为一个质数(大多数实现都使用 5381)
for (let i = 0; i < tableKey.length; i++) { // 迭代key
hash = (hash * 33) + tableKey.charCodeAt(i); // 将hash与33相乘再加上当前迭代到的字符的 ASCII码
}
return hash % 1013; // 最后,我们将使用相加的和与另一质数(1013)相除后的余数
}
const map = new Map();
map.set(‘Gandalf‘, ‘gandalf@email.com‘);
map.set(‘John‘, ‘johnsnow@email.com‘);
map.set(‘Tyrion‘, ‘tyrion@email.com‘);
console.log(map.has(‘Gandalf‘)); // true
console.log(map.size); // 3
console.log(map.keys()); // 输出{"Gandalf", "John", "Tyrion"}
console.log(map.values()); // 输出{"gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"}
console.log(map.get(‘Tyrion‘)); // tyrion@email.com
map.delete(‘Gandalf‘);
console.log(map.has(‘Gandalf‘)); // false
const map = new WeakMap();
const ob1 = { name: ‘Gandalf‘ };
const ob2 = { name: ‘John‘ };
const ob3 = { name: ‘Tyrion‘ };
map.set(ob1, ‘gandalf@email.com‘); //// WeakMap 类也可以用 set方法,但不能使用数、字符串、布尔值等基本数据类型,需要将名字转换为对象
map.set(ob2, ‘johnsnow@email.com‘);
map.set(ob3, ‘tyrion@email.com‘);
console.log(map.has(ob1)); // true
console.log(map.get(ob3)); // tyrion@email.com
map.delete(ob2); // 删除
console.log(map.has(ob2)); // false
【阅读笔记:散列表】Javascript任何对象都是一个散列表(hash表)!
原文:https://www.cnblogs.com/xingyongwang/p/11129410.html