algo/javascript/19_hashTable/hashtable.js
2019-06-25 13:59:51 +08:00

340 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****
* 带碰撞处理的Hash表
* 实际上在js中,单独实现一个Hash表感觉不是很有实用价值
* 如果需要通常是直接将Object,Map,Set来当Hash表用
*
* 总结:
* 我写的这个实现把store 从Object换成Array不会有运行性能上的区别
* 把hash函数改成生成一定范围的值的类型,然后初始化一个指定长度的数组因该会有一定的性能提升
* 把store换成Map然后修改相关实现会获得飞越性的提升因为在js中Map的实现对这种类型的操作做了优化
*/
class HashTable {
constructor() {
//创建一个没有原型链的对象
this.store = Object.create(null);
}
/**
* Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法主题是排序和搜索第6.4章。
* @param {*} string
* 翻译自别的语言的实现
* 需要注意的是由于js中没有int类型number是dobule的标准实现
* 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同
*/
hash(string) {
let len = string.length;
let hash = len;
for (let i = 0; i < len; i++) {
hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);
}
return hash & 0x7FFFFFFF;
}
isCresh(item) {
return Object.prototype.toString.call(item) === "[object Map]"
}
/**
* 约定item必须要有key
* @param {*} item
*/
put(item) {
if (typeof item.key !== 'string') {
throw 'item must have key!'
}
let hash = this.hash(item.key);
//碰撞处理
let cresh = this.store[hash];
if (cresh) {
if (cresh.key === item.key) {
this.store[hash] = item;
return
}
if (!this.isCresh(cresh)) {
this.store[hash] = new Map();
}
this.store[hash].set(item.key, item);
} else {
this.store[hash] = item;
}
}
get(key) {
let hash = this.hash(key);
let value = this.store[hash] || null;
if (this.isCresh(value)) {
return value.get(key);
} else {
return value
}
}
remove(key) {
let hash = this.hash(key);
let value = this.store[hash];
if (!value) {
return null;
}
if (this.isCresh(value)) {
value.delete(key);
} else {
delete this.store[hash];
}
}
clear() {
this.store = {};
}
print() {
let values = Object.values(this.store);
values.forEach(element => {
if (this.isCresh(element)) {
element.forEach(item => {
console.log(item);
});
} else {
console.log(element)
}
});
}
}
/**
* 相比使用Object和Array做store 运行时的性能提升了三分之一
* 但当前这种用法没有直接使用Map方便而且直接使用Map会快的多
*/
class HashTableBaseMap {
constructor() {
this.store = new Map();
}
/**
* Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法主题是排序和搜索第6.4章。
* @param {*} string
* 翻译自别的语言的实现
* 需要注意的是由于js中没有int类型number是dobule的标准实现
* 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同
*/
hash(string) {
let len = string.length;
let hash = len;
for (let i = 0; i < len; i++) {
hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);
}
return hash & 0x7FFFFFFF;
}
isCresh(item) {
return Object.prototype.toString.call(item) === "[object Map]"
}
/**
* 约定item必须要有key
* @param {*} item
*/
put(item) {
if (typeof item.key !== 'string') {
throw 'item must have key!'
}
let hash = this.hash(item.key);
//碰撞处理
let cresh = this.store.get(hash);
if (cresh) {
if (cresh.key === item.key) {
this.store.set(hash, item);
return
}
if (!this.isCresh(cresh)) {
this.store[hash] = new Map();
}
this.store[hash].set(item.key, item);
} else {
this.store.set(hash, item);
}
}
get(key) {
let hash = this.hash(key);
let value = this.store.get(hash);
if (this.isCresh(value)) {
return value.get(key);
} else {
return value
}
}
remove(key) {
let hash = this.hash(key);
let value = this.store.get(hash);
if (!value) {
return null;
}
if (this.isCresh(value)) {
value.delete(key);
} else {
this.store.delete(hash)
}
}
clear() {
this.store = {};
}
print() {
this.store.forEach(element => {
if (this.isCresh(element)) {
element.forEach(item => {
console.log(item);
});
} else {
console.log(element)
}
});
}
}
/**
* 基础测试
*/
function baseTest() {
let hashTable = new HashTable();
for (let i = 0; i < 10; i++) {
hashTable.put({
key: 'test' + i,
value: 'some value' + i
});
}
console.log('step1:')
//随机获取5次
for (let j = 0; j < 5; j++) {
let key = 'test' + Math.floor(Math.random() * 10);
console.log(key);
console.log(hashTable.get(key))
}
//获得一次空值
console.log('get null:', hashTable.get('test10'))
//修改一次值
hashTable.put({
key: 'test1',
value: 'change'
});
//删除一次值
hashTable.remove('test2');
console.log('step2:')
//输出修改后所有的
hashTable.print();
}
/**
* 有序key存取性能测试
*/
function ordKeyTest() {
let length = 1000000;
console.time('create')
let hashTable = new HashTable();
for (let i = 0; i < length; i++) {
//24位长度有序key
hashTable.put({
key: 'someTestSoSoSoSoLongKey' + i,
value: 'some value' + i
});
}
console.timeEnd('create')
let get = 100000;
console.time('get')
for (let j = 0; j < get; j++) {
let key = 'test' + Math.floor(Math.random() * 999999);
hashTable.get(key)
}
console.timeEnd('get')
}
/**
* 无序key性能测试
* 这个查找稍微有点不准,会有一定量随机字符串重复
* 实际结果创建没有区别大数据量下由于无序key有一些会碰撞get的总体用的时间会多不少。
*/
function randKeyTest() {
let length = 1000000;
let keyList = [];
for (let i = 0; i < length; i++) {
keyList.push(randomString());
}
console.time('create')
let hashTable = new HashTable();
for (let i = 0; i < length; i++) {
hashTable.put({
key: keyList[i],
value: 'some value' + i
});
}
console.timeEnd('create')
let get = 100000;
console.time('get')
for (let j = 0; j < get; j++) {
let key = keyList[Math.floor(Math.random() * 999999)];
hashTable.get(key)
}
console.timeEnd('get')
}
/**
* 直接使用Object的性能测试
* 有序就不测了估计不会有区别只看不使用hash的无序key
* 结果:想达到同样的结果创建会比hash后的慢接近四分之三,获取用时差不多
*/
function randKeyTestFromObj() {
let length = 1000000;
let keyList = [];
for (let i = 0; i < length; i++) {
keyList.push(randomString());
}
console.time('create')
let hashTable = {};
for (let i = 0; i < length; i++) {
let key = keyList[i];
hashTable[key] = {
key: key,
value: 'some value' + i
}
}
console.timeEnd('create')
let get = 100000;
console.time('get')
for (let j = 0; j < get; j++) {
let key = keyList[Math.floor(Math.random() * 999999)];
hashTable[key]
}
console.timeEnd('get')
}
/**
* 直接使用Map的性能测试
* 结果:创建用时差不多,但是获取快了一个数量级(十倍不止)
*/
function randKeyTestFromMap() {
let length = 1000000;
let keyList = [];
for (let i = 0; i < length; i++) {
keyList.push(randomString());
}
console.time('create')
let hashTable = new Map();
for (let i = 0; i < length; i++) {
let key = keyList[i];
hashTable.set(key, {
key: key,
value: 'some value' + i
})
}
console.timeEnd('create')
let get = 100000;
console.time('get')
for (let j = 0; j < get; j++) {
let key = keyList[Math.floor(Math.random() * 999999)];
hashTable.get(key);
}
console.timeEnd('get')
}
//生成指定长度的字符串
function randomString(len) {
len = len || 24;
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPos = chars.length;
var pwd = '';
for (i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}