class HashTable {
constructor(size = 0, lambda_min = 0.5, lambda_max = 0.75, size_min = 4) {
this.table = new Array(Math.max(size, size_min));
this.n = 0;
this.h = makeHash(this.m);
Object.assign(this, { lambda_min, lambda_max, size_min });
}
get m() {
return this.table.length;
}
get lambda() {
return this.n / this.m;
}
find(key) {
let i = this.h(key);
let cell = this.table[i];
while (cell && cell.key != key) cell = cell.next;
if (cell) return cell.value;
return null;
}
insert(key, value) {
let i = this.h(key);
let cell = this.table[i];
while (cell && cell.key != key) cell = cell.next;
if (cell) throw `${key} already in table`;
this.n++;
this.table[i] = { key, value, next: this.table[i] };
if (this.lambda > this.lambda_max) this.rehash();
}
delete(key) {
let i = this.h(key);
let cell = this.table[i];
let prev = null;
while (cell && cell.key != key) {
prev = cell;
cell = cell.next;
}
if (!cell) throw `${key} not in table`;
this.n--;
if (prev) {
prev.next = cell.next;
} else {
if (cell) this.table[i] = cell.next;
else this.table[i] = undefined;
}
if (this.lambda < this.lambda_min) this.rehash();
}
*cells() {
for (let cell of this.table) {
while (cell) {
yield cell;
cell = cell.next;
}
}
}
rehash() {
const lambda_0 = (this.lambda_max + this.lambda_min) / 2;
const newSize = Math.max(this.size_min, Math.round(this.n / lambda_0));
if (newSize == this.m) return;
const newTable = new HashTable(
newSize,
this.lambda_min,
this.lambda_max,
this.size_min
);
for (let cell of this.cells()) {
newTable.insert(cell.key, cell.value);
}
Object.assign(this, newTable);
}
}