@@ -6,12 +6,13 @@ class HashMap {
6
6
7
7
/**
8
8
* Initialize array that holds the values. Default is size 1,000
9
- * @param {number} size
9
+ * @param {number} initialCapacity
10
10
*/
11
- constructor(size = 1000) {
12
- this.buckets = new Array(size );
11
+ constructor(initialCapacity = 1000) {
12
+ this.buckets = new Array(initialCapacity );
13
13
this.size = 0;
14
14
this.collisions = 0;
15
+ this.keys = [];
15
16
}
16
17
17
18
/**
@@ -20,10 +21,10 @@ class HashMap {
20
21
*/
21
22
hash(key) {
22
23
let hashValue = 0;
23
- const stringKey = key.toString() ;
24
+ const stringTypeKey = `${ key}${typeof key}` ;
24
25
25
- for (let index = 0; index < stringKey .length; index++) {
26
- const charCode = stringKey .charCodeAt(index);
26
+ for (let index = 0; index < stringTypeKey .length; index++) {
27
+ const charCode = stringTypeKey .charCodeAt(index);
27
28
hashValue += charCode << (index * 8);
28
29
}
29
30
@@ -51,8 +52,9 @@ class HashMap {
51
52
52
53
if(entryIndex === undefined) {
53
54
// initialize array and save key/value
55
+ const keyIndex = this.keys.push({content: key}) - 1; // keep track of the key index
54
56
this.buckets[bucketIndex] = this.buckets[bucketIndex] || [];
55
- this.buckets[bucketIndex].push({key, value});
57
+ this.buckets[bucketIndex].push({key, value, keyIndex });
56
58
this.size++;
57
59
// Optional: keep count of collisions
58
60
if(this.buckets[bucketIndex].length > 1) { this.collisions++; }
@@ -111,22 +113,43 @@ class HashMap {
111
113
* @param {any} key
112
114
*/
113
115
delete(key) {
114
- const {bucketIndex, entryIndex} = this._getIndexes(key);
116
+ const {bucketIndex, entryIndex, keyIndex } = this._getIndexes(key);
115
117
116
118
if(entryIndex === undefined) {
117
119
return false;
118
120
}
119
121
120
122
this.buckets[bucketIndex].splice(entryIndex, 1);
123
+ delete this.keys[keyIndex];
121
124
this.size--;
122
125
123
126
return true;
124
127
}
128
+
129
+ /**
130
+ * Rehash means to create a new Map with a new (higher) capacity with the purpose of outgrow collisions.
131
+ * @param {Number} newCapacity
132
+ */
133
+ rehash(newCapacity) {
134
+ const newMap = new HashMap(newCapacity);
135
+
136
+ this.keys.forEach(key => {
137
+ if(key) {
138
+ newMap.set(key.content, this.get(key.content));
139
+ }
140
+ });
141
+
142
+ // update bucket
143
+ this.buckets = newMap.buckets;
144
+ this.collisions = newMap.collisions;
145
+ // Optional: both `keys` has the same content except that the new one doesn't have empty spaces from deletions
146
+ this.keys = newMap.keys;
147
+ }
125
148
}
126
149
127
150
// Usage:
128
- const hashMap = new HashMap();
129
- // const hashMap = new HashMap(1);
151
+ // const hashMap = new HashMap();
152
+ const hashMap = new HashMap(1);
130
153
// const hashMap = new Map();
131
154
132
155
const assert = require('assert');
@@ -156,6 +179,18 @@ hashMap.set('art', 2);
156
179
assert.equal(hashMap.get('art'), 2);
157
180
assert.equal(hashMap.size, 3);
158
181
182
+ // undefined
183
+ hashMap.set(undefined, 'undefined type');
184
+ hashMap.set('undefined', 'string type');
185
+
186
+ assert.equal(hashMap.get(undefined), 'undefined type');
187
+ assert.equal(hashMap.get('undefined'), 'string type');
188
+
159
189
// internal structure
160
190
console.log(hashMap.collisions);
161
- console.log(hashMap.buckets);
191
+ console.log(hashMap.buckets);
192
+
193
+ // rehash
194
+ hashMap.rehash();
195
+ console.log(hashMap.collisions);
196
+ console.log(hashMap.buckets);
0 commit comments