1 | import { NullPointerException } from "../exception/NotNullException";
|
2 | import LinkList from "../linklist/LinkList";
|
3 | import { hash, toString } from "../util";
|
4 | function defineHashNodeToString(node) {
|
5 | Object.defineProperty(node, "toString", {
|
6 | value: function () {
|
7 | return JSON.stringify(this);
|
8 | },
|
9 | });
|
10 | }
|
11 | export class HashTable {
|
12 | constructor(size = HashTable.DEFAULT_TABLE_SIZE) {
|
13 | this.buckets = Array(size).fill(null).map(() => new LinkList());
|
14 | this.count = 0;
|
15 | this.keys = {};
|
16 | this.threshold = size * HashTable.LOADFACTOR;
|
17 | }
|
18 | static setDefaultTableSize(size) {
|
19 | this.DEFAULT_TABLE_SIZE = size;
|
20 | }
|
21 | get Count() {
|
22 | return this.count;
|
23 | }
|
24 | get TableSize() {
|
25 | return this.buckets.length;
|
26 | }
|
27 | put(key, value) {
|
28 | if (key === null || key === undefined) {
|
29 | throw new NullPointerException();
|
30 | }
|
31 | const tempKey = toString(key);
|
32 | const hashed = hash(tempKey);
|
33 | let keyHash = this.mod(hashed);
|
34 | let bucketLinkedList = this.buckets[keyHash];
|
35 | const node = bucketLinkedList.findNode(item => item.key === key);
|
36 | if (node) {
|
37 | node.Value.value = value;
|
38 | return;
|
39 | }
|
40 | if (this.count >= this.threshold) {
|
41 | this.rehash();
|
42 | keyHash = this.mod(hashed);
|
43 | bucketLinkedList = this.buckets[keyHash];
|
44 | }
|
45 | this.keys[tempKey] = keyHash;
|
46 | bucketLinkedList.append({
|
47 | key,
|
48 | value,
|
49 | });
|
50 | this.count++;
|
51 | return this;
|
52 | }
|
53 | get(key) {
|
54 | const tempKey = toString(key);
|
55 | const hashed = hash(tempKey);
|
56 | const keyHash = this.mod(hashed);
|
57 | const bucketLinkedList = this.buckets[keyHash];
|
58 | const node = bucketLinkedList.findNode(item => item.key === key);
|
59 | return node ? node.Value.value : null;
|
60 | }
|
61 | remove(key) {
|
62 | const tempKey = toString(key);
|
63 | const hashed = hash(tempKey);
|
64 | const keyHash = this.mod(hashed);
|
65 | const bucketLinkedList = this.buckets[keyHash];
|
66 | const node = bucketLinkedList.findNode(item => item.key === key);
|
67 | if (node) {
|
68 | this.count--;
|
69 | return bucketLinkedList.deleteNode(node.Value);
|
70 | }
|
71 | return false;
|
72 | }
|
73 | contains(key) {
|
74 | return this.get(key) !== null;
|
75 | }
|
76 | getKeys() {
|
77 | return Object.keys(this.keys);
|
78 | }
|
79 | getOrignalKeys() {
|
80 | const arr = new Array(this.count);
|
81 | this.iterate((item, index) => {
|
82 | arr[index] = item.key;
|
83 | });
|
84 | return arr;
|
85 | }
|
86 | values() {
|
87 | const arr = new Array(this.count);
|
88 | this.iterate((item, index) => {
|
89 | arr[index] = item.value;
|
90 | });
|
91 | return arr;
|
92 | }
|
93 | clear() {
|
94 | this.buckets.length = 0;
|
95 | this.keys = {};
|
96 | this.count = 0;
|
97 | this.buckets = Array(HashTable.DEFAULT_TABLE_SIZE)
|
98 | .fill(null).map(() => new LinkList());
|
99 | }
|
100 | getHashKey(key) {
|
101 | const tempKey = toString(key);
|
102 | return this.keys[tempKey];
|
103 | }
|
104 | toString() {
|
105 | const arr = [];
|
106 | this.iterate(item => {
|
107 | const node = {
|
108 | key: item.key,
|
109 | value: item.value,
|
110 | };
|
111 | defineHashNodeToString(node);
|
112 | arr.push(node);
|
113 | });
|
114 | return arr.toString();
|
115 | }
|
116 | iterate(fn) {
|
117 | let iterateFlag = 0;
|
118 | for (let i = 0, count = this.buckets.length; i < count; i++) {
|
119 | const linkArr = this.buckets[i].toArray();
|
120 | const arrCount = iterateFlag;
|
121 | for (let j = arrCount, addCount = arrCount + linkArr.length; j < addCount; j++) {
|
122 | fn(linkArr[j - arrCount].Value, iterateFlag);
|
123 | iterateFlag++;
|
124 | }
|
125 | }
|
126 | }
|
127 | rehash() {
|
128 | const oldBuckets = this.buckets;
|
129 | const newCapacity = oldBuckets.length * 2 + 1;
|
130 | const newBuckets = Array(newCapacity).fill(null).map(() => new LinkList());
|
131 | this.buckets = newBuckets;
|
132 | this.keys = {};
|
133 | for (let i = 0, oldLen = oldBuckets.length; i < oldLen; i++) {
|
134 | oldBuckets[i].toArray().forEach(item => {
|
135 | const data = item.Value;
|
136 | const hashed = hash(toString(data.key));
|
137 | const keyHash = this.mod(hashed);
|
138 | newBuckets[keyHash].append(data);
|
139 | this.keys[toString(data.key)] = keyHash;
|
140 | });
|
141 | }
|
142 | oldBuckets.length = 0;
|
143 | this.threshold = newCapacity * HashTable.LOADFACTOR;
|
144 | }
|
145 | mod(hashed) {
|
146 | const modulo = hashed % this.buckets.length;
|
147 | return hashed < 0 ? modulo * -1 : modulo;
|
148 | }
|
149 | }
|
150 | HashTable.DEFAULT_TABLE_SIZE = 11;
|
151 | HashTable.LOADFACTOR = 0.75;
|