UNPKG

6.76 kBPlain TextView Raw
1import { NullPointerException } from "../exception/NotNullException";
2import LinkList from "../linklist/LinkList";
3import { hash, toString } from "../util";
4function defineHashNodeToString<T>(node: {key: any, value: T}){
5 Object.defineProperty(node, "toString", {
6 // tslint:disable-next-line:object-literal-shorthand
7 value: function(){
8 return JSON.stringify(this);
9 },
10 });
11}
12/**
13 * 哈希表
14 */
15export class HashTable<T>{
16 private buckets: Array<LinkList<{key: string, value: T}>>;
17 private count: number;
18 /**
19 * 扩容参数 用于判断是否需要扩容
20 */
21 private threshold: number;
22 private keys: {[index: string]: number};
23 private static DEFAULT_TABLE_SIZE = 11;
24 /**
25 * 加载因子 用于重哈希时扩容哈希表
26 */
27 private static readonly LOADFACTOR = 0.75;
28
29 static setDefaultTableSize(size: number){
30 this.DEFAULT_TABLE_SIZE = size;
31 }
32
33 public get Count(){
34 return this.count;
35 }
36
37 public get TableSize(){
38 return this.buckets.length;
39 }
40
41 /**
42 * 哈希表
43 * @param size 指定默认哈希表大小
44 */
45 constructor(size = HashTable.DEFAULT_TABLE_SIZE){
46 this.buckets = Array(size).fill(null).map(() => new LinkList<{key: any, value: T}>());
47 this.count = 0;
48 this.keys = {};
49 this.threshold = size * HashTable.LOADFACTOR;
50 }
51
52 /**
53 * 存放键值
54 * @param key 区分基础类型和对象类型,对象使用json序列化
55 * @param value 值
56 */
57 public put(key: any, value: T){
58 if (key === null || key === undefined){
59 throw new NullPointerException();
60 }
61 const tempKey = toString(key);
62 // 查找对应的hash的链表
63 const hashed = hash(tempKey);
64 let keyHash = this.mod(hashed);
65 // 取余后,指定索引位置
66 let bucketLinkedList = this.buckets[keyHash];
67 // 查找链表,判断节点是否存在
68 const node = bucketLinkedList.findNode(item => item.key === key);
69 if (node){
70 node.Value.value = value;
71 return;
72 }
73 // 判断数量是否大于扩容因子,然后扩容,重哈希。
74 if (this.count >= this.threshold){
75 this.rehash();
76 keyHash = this.mod(hashed);
77 bucketLinkedList = this.buckets[keyHash];
78 }
79 this.keys[tempKey] = keyHash;
80 bucketLinkedList.append({
81 key,
82 value,
83 });
84 this.count++;
85 return this;
86 }
87
88 /**
89 * 获取指定键对应的值
90 * @param key
91 */
92 public get(key: any): T{
93 const tempKey = toString(key);
94 const hashed = hash(tempKey);
95 const keyHash = this.mod(hashed);
96 const bucketLinkedList = this.buckets[keyHash];
97 const node = bucketLinkedList.findNode(item => item.key === key);
98 return node ? node.Value.value : null;
99 }
100
101 /**
102 * 移出指定键
103 * @param key
104 */
105 public remove(key: any): boolean{
106 const tempKey = toString(key);
107 const hashed = hash(tempKey);
108 const keyHash = this.mod(hashed);
109 const bucketLinkedList = this.buckets[keyHash];
110 const node = bucketLinkedList.findNode(item => item.key === key);
111 if (node){
112 this.count--;
113 // TODO:没有rehash
114 return bucketLinkedList.deleteNode(node.Value);
115 }
116 return false;
117 }
118
119 public contains(key: any): boolean{
120 return this.get(key) !== null;
121 }
122
123 /**
124 * 获取所有的序列化后的key
125 */
126 public getKeys(): Array<string>{
127 return Object.keys(this.keys);
128 }
129
130 /**
131 * 获取所有原始的key
132 */
133 public getOrignalKeys(): Array<any>{
134 const arr = new Array<any>(this.count);
135 this.iterate((item, index) => {
136 arr[index] = item.key;
137 });
138 return arr;
139 }
140
141 /**
142 * 获取所有的值
143 */
144 public values(): Array<T>{
145 const arr = new Array<T>(this.count);
146 this.iterate((item, index) => {
147 arr[index] = item.value;
148 });
149 return arr;
150 }
151
152 /**
153 * 清空哈希表
154 */
155 public clear(){
156 this.buckets.length = 0;
157 this.keys = {};
158 this.count = 0;
159 this.buckets = Array(HashTable.DEFAULT_TABLE_SIZE)
160 .fill(null).map(() => new LinkList<{key: string, value: T}>());
161 }
162
163 /**
164 * 获取指定键对应的哈希值
165 * @param key
166 */
167 public getHashKey(key: any){
168 const tempKey = toString(key);
169 return this.keys[tempKey];
170 }
171
172 public toString(){
173 const arr: Array<{key: string, value: T}> = [];
174 this.iterate(item => {
175 const node = {
176 key: item.key,
177 value: item.value,
178 };
179 defineHashNodeToString(node);
180 arr.push(node);
181 });
182 return arr.toString();
183 }
184
185 /**
186 * 哈希表迭代器
187 * @param fn
188 */
189 private iterate(fn: (item: {key: string, value: T}, index?: number) => void){
190 let iterateFlag = 0;
191 // 循环所有的链表,取出对应的值
192 for (let i = 0, count = this.buckets.length; i < count ; i++){
193 const linkArr = this.buckets[i].toArray();
194 const arrCount = iterateFlag;
195 for (let j = arrCount, addCount = arrCount + linkArr.length; j < addCount ; j++){
196 fn(linkArr[j - arrCount].Value, iterateFlag);
197 iterateFlag++;
198 }
199 }
200 }
201
202 /**
203 * 重哈希
204 */
205 private rehash(){
206 const oldBuckets = this.buckets;
207 // 扩容为原来的2n+1倍
208 const newCapacity = oldBuckets.length * 2 + 1;
209 const newBuckets = Array(newCapacity).fill(null).map(() => new LinkList<{key: string, value: T}>());
210 this.buckets = newBuckets;
211 this.keys = {};
212 // 将原来的所有数据重新计算哈希,然后放入新的哈希表中
213 for (let i = 0 , oldLen = oldBuckets.length; i < oldLen; i++){
214 oldBuckets[i].toArray().forEach(item => {
215 const data = item.Value;
216 const hashed = hash(toString(data.key));
217 const keyHash = this.mod(hashed);
218 newBuckets[keyHash].append(data);
219 this.keys[toString(data.key)] = keyHash;
220 });
221 }
222 oldBuckets.length = 0;
223 // 重新计算扩容因子
224 this.threshold = newCapacity * HashTable.LOADFACTOR;
225 }
226
227 private mod(hashed: number){
228 const modulo = hashed % this.buckets.length;
229 return hashed < 0 ? modulo * -1 : modulo;
230 }
231}