Skip to content

Commit cd81d6a

Browse files
committed
添加一个js的hashtable实现
1 parent e48be29 commit cd81d6a

File tree

1 file changed

+340
-0
lines changed

1 file changed

+340
-0
lines changed
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
/****
2+
* 带碰撞处理的Hash表
3+
* 实际上在js中,单独实现一个Hash表感觉不是很有实用价值
4+
* 如果需要通常是直接将Object,Map,Set来当Hash表用
5+
*
6+
* 总结:
7+
* 我写的这个实现把store 从Object换成Array不会有运行性能上的区别
8+
* 把hash函数改成生成一定范围的值的类型,然后初始化一个指定长度的数组因该会有一定的性能提升
9+
* 把store换成Map,然后修改相关实现会获得飞越性的提升,因为在js中Map的实现对这种类型的操作做了优化
10+
*/
11+
class HashTable {
12+
constructor() {
13+
//创建一个没有原型链的对象
14+
this.store = Object.create(null);
15+
}
16+
/**
17+
* Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。
18+
* @param {*} string
19+
* 翻译自别的语言的实现
20+
* 需要注意的是由于js中没有int类型,number是dobule的标准实现
21+
* 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同
22+
*/
23+
hash(string) {
24+
let len = string.length;
25+
let hash = len;
26+
for (let i = 0; i < len; i++) {
27+
hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);
28+
}
29+
return hash & 0x7FFFFFFF;
30+
}
31+
32+
isCresh(item) {
33+
return Object.prototype.toString.call(item) === "[object Map]"
34+
}
35+
/**
36+
* 约定item必须要有key
37+
* @param {*} item
38+
*/
39+
put(item) {
40+
if (typeof item.key !== 'string') {
41+
throw 'item must have key!'
42+
}
43+
let hash = this.hash(item.key);
44+
//碰撞处理
45+
let cresh = this.store[hash];
46+
if (cresh) {
47+
if (cresh.key === item.key) {
48+
this.store[hash] = item;
49+
return
50+
}
51+
52+
if (!this.isCresh(cresh)) {
53+
this.store[hash] = new Map();
54+
}
55+
this.store[hash].set(item.key, item);
56+
} else {
57+
this.store[hash] = item;
58+
}
59+
}
60+
get(key) {
61+
let hash = this.hash(key);
62+
let value = this.store[hash] || null;
63+
if (this.isCresh(value)) {
64+
return value.get(key);
65+
} else {
66+
return value
67+
}
68+
}
69+
remove(key) {
70+
let hash = this.hash(key);
71+
let value = this.store[hash];
72+
if (!value) {
73+
return null;
74+
}
75+
if (this.isCresh(value)) {
76+
value.delete(key);
77+
} else {
78+
delete this.store[hash];
79+
}
80+
}
81+
clear() {
82+
this.store = {};
83+
}
84+
print() {
85+
let values = Object.values(this.store);
86+
values.forEach(element => {
87+
if (this.isCresh(element)) {
88+
element.forEach(item => {
89+
console.log(item);
90+
});
91+
} else {
92+
console.log(element)
93+
}
94+
});
95+
}
96+
}
97+
/**
98+
* 相比使用Object和Array做store 运行时的性能提升了三分之一
99+
* 但当前这种用法没有直接使用Map方便,而且直接使用Map会快的多
100+
*/
101+
class HashTableBaseMap {
102+
constructor() {
103+
this.store = new Map();
104+
}
105+
/**
106+
* Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。
107+
* @param {*} string
108+
* 翻译自别的语言的实现
109+
* 需要注意的是由于js中没有int类型,number是dobule的标准实现
110+
* 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同
111+
*/
112+
hash(string) {
113+
let len = string.length;
114+
let hash = len;
115+
for (let i = 0; i < len; i++) {
116+
hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);
117+
}
118+
return hash & 0x7FFFFFFF;
119+
}
120+
121+
isCresh(item) {
122+
return Object.prototype.toString.call(item) === "[object Map]"
123+
}
124+
/**
125+
* 约定item必须要有key
126+
* @param {*} item
127+
*/
128+
put(item) {
129+
if (typeof item.key !== 'string') {
130+
throw 'item must have key!'
131+
}
132+
let hash = this.hash(item.key);
133+
//碰撞处理
134+
let cresh = this.store.get(hash);
135+
if (cresh) {
136+
if (cresh.key === item.key) {
137+
this.store.set(hash, item);
138+
return
139+
}
140+
141+
if (!this.isCresh(cresh)) {
142+
this.store[hash] = new Map();
143+
}
144+
this.store[hash].set(item.key, item);
145+
} else {
146+
this.store.set(hash, item);
147+
}
148+
}
149+
get(key) {
150+
let hash = this.hash(key);
151+
let value = this.store.get(hash);
152+
if (this.isCresh(value)) {
153+
return value.get(key);
154+
} else {
155+
return value
156+
}
157+
}
158+
remove(key) {
159+
let hash = this.hash(key);
160+
let value = this.store.get(hash);
161+
if (!value) {
162+
return null;
163+
}
164+
if (this.isCresh(value)) {
165+
value.delete(key);
166+
} else {
167+
this.store.delete(hash)
168+
}
169+
}
170+
clear() {
171+
this.store = {};
172+
}
173+
print() {
174+
this.store.forEach(element => {
175+
if (this.isCresh(element)) {
176+
element.forEach(item => {
177+
console.log(item);
178+
});
179+
} else {
180+
console.log(element)
181+
}
182+
});
183+
}
184+
}
185+
186+
/**
187+
* 基础测试
188+
*/
189+
function baseTest() {
190+
let hashTable = new HashTable();
191+
for (let i = 0; i < 10; i++) {
192+
hashTable.put({
193+
key: 'test' + i,
194+
value: 'some value' + i
195+
});
196+
}
197+
console.log('step1:')
198+
//随机获取5次
199+
for (let j = 0; j < 5; j++) {
200+
let key = 'test' + Math.floor(Math.random() * 10);
201+
console.log(key);
202+
console.log(hashTable.get(key))
203+
}
204+
//获得一次空值
205+
console.log('get null:', hashTable.get('test10'))
206+
//修改一次值
207+
hashTable.put({
208+
key: 'test1',
209+
value: 'change'
210+
});
211+
//删除一次值
212+
hashTable.remove('test2');
213+
console.log('step2:')
214+
//输出修改后所有的
215+
hashTable.print();
216+
}
217+
218+
/**
219+
* 有序key存取,性能测试
220+
*/
221+
function ordKeyTest() {
222+
let length = 1000000;
223+
console.time('create')
224+
let hashTable = new HashTable();
225+
for (let i = 0; i < length; i++) {
226+
//24位长度有序key
227+
hashTable.put({
228+
key: 'someTestSoSoSoSoLongKey' + i,
229+
value: 'some value' + i
230+
});
231+
}
232+
console.timeEnd('create')
233+
234+
let get = 100000;
235+
console.time('get')
236+
for (let j = 0; j < get; j++) {
237+
let key = 'test' + Math.floor(Math.random() * 999999);
238+
hashTable.get(key)
239+
}
240+
console.timeEnd('get')
241+
}
242+
243+
/**
244+
* 无序key性能测试
245+
* 这个查找稍微有点不准,会有一定量随机字符串重复
246+
* 实际结果,创建没有区别,大数据量下由于无序key有一些会碰撞,get的总体用的时间会多不少。
247+
*/
248+
function randKeyTest() {
249+
let length = 1000000;
250+
let keyList = [];
251+
for (let i = 0; i < length; i++) {
252+
keyList.push(randomString());
253+
}
254+
console.time('create')
255+
let hashTable = new HashTable();
256+
for (let i = 0; i < length; i++) {
257+
hashTable.put({
258+
key: keyList[i],
259+
value: 'some value' + i
260+
});
261+
}
262+
console.timeEnd('create')
263+
let get = 100000;
264+
console.time('get')
265+
for (let j = 0; j < get; j++) {
266+
let key = keyList[Math.floor(Math.random() * 999999)];
267+
hashTable.get(key)
268+
}
269+
console.timeEnd('get')
270+
}
271+
272+
/**
273+
* 直接使用Object的性能测试
274+
* 有序就不测了,估计不会有区别,只看不使用hash的无序key
275+
* 结果:想达到同样的结果创建会比hash后的慢接近四分之三,获取用时差不多
276+
*/
277+
function randKeyTestFromObj() {
278+
let length = 1000000;
279+
let keyList = [];
280+
for (let i = 0; i < length; i++) {
281+
keyList.push(randomString());
282+
}
283+
console.time('create')
284+
let hashTable = {};
285+
for (let i = 0; i < length; i++) {
286+
let key = keyList[i];
287+
hashTable[key] = {
288+
key: key,
289+
value: 'some value' + i
290+
}
291+
}
292+
console.timeEnd('create')
293+
let get = 100000;
294+
console.time('get')
295+
for (let j = 0; j < get; j++) {
296+
let key = keyList[Math.floor(Math.random() * 999999)];
297+
hashTable[key]
298+
}
299+
console.timeEnd('get')
300+
}
301+
/**
302+
* 直接使用Map的性能测试
303+
* 结果:创建用时差不多,但是获取快了一个数量级(十倍不止)
304+
*/
305+
function randKeyTestFromMap() {
306+
let length = 1000000;
307+
let keyList = [];
308+
for (let i = 0; i < length; i++) {
309+
keyList.push(randomString());
310+
}
311+
console.time('create')
312+
let hashTable = new Map();
313+
for (let i = 0; i < length; i++) {
314+
let key = keyList[i];
315+
hashTable.set(key, {
316+
key: key,
317+
value: 'some value' + i
318+
})
319+
}
320+
console.timeEnd('create')
321+
let get = 100000;
322+
console.time('get')
323+
for (let j = 0; j < get; j++) {
324+
let key = keyList[Math.floor(Math.random() * 999999)];
325+
hashTable.get(key);
326+
}
327+
console.timeEnd('get')
328+
}
329+
330+
//生成指定长度的字符串
331+
function randomString(len) {
332+
len = len || 24;
333+
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
334+
var maxPos = chars.length;
335+
var pwd = '';
336+
for (i = 0; i < len; i++) {
337+
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
338+
}
339+
return pwd;
340+
}

0 commit comments

Comments
 (0)