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