Initial Commit - Sonnet 4.5
This commit is contained in:
279
src/core/HashMap.ts
Normal file
279
src/core/HashMap.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import type { IHashMap } from "../interfaces/IHashMap.ts";
|
||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
||||
import { HashNode } from "../models/HashNode.ts";
|
||||
import { DefaultHashFunction } from "../hash-functions/DefaultHashFunction.ts";
|
||||
|
||||
/**
|
||||
* HashMap implementation using separate chaining for collision resolution.
|
||||
* Follows SOLID principles:
|
||||
* - SRP: Focused on hash map operations
|
||||
* - OCP: Extensible through custom hash functions
|
||||
* - LSP: Implements IHashMap interface correctly
|
||||
* - ISP: Uses focused interfaces
|
||||
* - DIP: Depends on IHashFunction abstraction
|
||||
*
|
||||
* @template K - The type of keys
|
||||
* @template V - The type of values
|
||||
*/
|
||||
export class HashMap<K, V> implements IHashMap<K, V>, Iterable<[K, V]> {
|
||||
private buckets: (HashNode<K, V> | null)[];
|
||||
private _size: number = 0;
|
||||
private readonly hashFunction: IHashFunction<K>;
|
||||
private readonly loadFactorThreshold: number;
|
||||
private readonly initialCapacity: number;
|
||||
|
||||
/**
|
||||
* Creates a new HashMap instance.
|
||||
* @param initialCapacity - Initial number of buckets (default: 16)
|
||||
* @param loadFactorThreshold - Threshold for resizing (default: 0.75)
|
||||
* @param hashFunction - Custom hash function (optional)
|
||||
*/
|
||||
constructor(
|
||||
initialCapacity: number = 16,
|
||||
loadFactorThreshold: number = 0.75,
|
||||
hashFunction?: IHashFunction<K>
|
||||
) {
|
||||
if (initialCapacity <= 0) {
|
||||
throw new Error("Initial capacity must be positive");
|
||||
}
|
||||
if (loadFactorThreshold <= 0 || loadFactorThreshold > 1) {
|
||||
throw new Error("Load factor must be between 0 and 1");
|
||||
}
|
||||
|
||||
this.initialCapacity = initialCapacity;
|
||||
this.buckets = new Array(initialCapacity).fill(null);
|
||||
this.loadFactorThreshold = loadFactorThreshold;
|
||||
this.hashFunction = hashFunction ?? new DefaultHashFunction<K>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current number of key-value pairs in the map.
|
||||
*/
|
||||
get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current capacity (number of buckets).
|
||||
*/
|
||||
get capacity(): number {
|
||||
return this.buckets.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current load factor.
|
||||
*/
|
||||
get loadFactor(): number {
|
||||
return this._size / this.buckets.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts or updates a key-value pair in the map.
|
||||
* Time Complexity: Average O(1), Worst O(n)
|
||||
*/
|
||||
set(key: K, value: V): void {
|
||||
this.ensureCapacity();
|
||||
|
||||
const index = this.hashFunction.hash(key, this.buckets.length);
|
||||
let node = this.buckets[index] ?? null;
|
||||
|
||||
// Check if key already exists
|
||||
while (node !== null) {
|
||||
if (this.keysEqual(node.key, key)) {
|
||||
node.value = value; // Update existing value
|
||||
return;
|
||||
}
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
// Insert new node at the beginning of the chain
|
||||
const newNode = new HashNode(key, value, this.buckets[index] ?? null);
|
||||
this.buckets[index] = newNode;
|
||||
this._size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value associated with the given key.
|
||||
* Time Complexity: Average O(1), Worst O(n)
|
||||
*/
|
||||
get(key: K): V | undefined {
|
||||
const index = this.hashFunction.hash(key, this.buckets.length);
|
||||
let node = this.buckets[index] ?? null;
|
||||
|
||||
while (node !== null) {
|
||||
if (this.keysEqual(node.key, key)) {
|
||||
return node.value;
|
||||
}
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a key exists in the map.
|
||||
* Time Complexity: Average O(1), Worst O(n)
|
||||
*/
|
||||
has(key: K): boolean {
|
||||
const index = this.hashFunction.hash(key, this.buckets.length);
|
||||
let node = this.buckets[index] ?? null;
|
||||
|
||||
while (node !== null) {
|
||||
if (this.keysEqual(node.key, key)) {
|
||||
return true;
|
||||
}
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a key-value pair from the map.
|
||||
* Time Complexity: Average O(1), Worst O(n)
|
||||
*/
|
||||
delete(key: K): boolean {
|
||||
const index = this.hashFunction.hash(key, this.buckets.length);
|
||||
let node = this.buckets[index] ?? null;
|
||||
let prev: HashNode<K, V> | null = null;
|
||||
|
||||
while (node !== null) {
|
||||
if (this.keysEqual(node.key, key)) {
|
||||
if (prev === null) {
|
||||
// Remove head node
|
||||
this.buckets[index] = node.next;
|
||||
} else {
|
||||
// Remove middle or tail node
|
||||
prev.next = node.next;
|
||||
}
|
||||
this._size--;
|
||||
return true;
|
||||
}
|
||||
prev = node;
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key-value pairs from the map.
|
||||
* Time Complexity: O(n) where n is the capacity
|
||||
*/
|
||||
clear(): void {
|
||||
this.buckets = new Array(this.initialCapacity).fill(null);
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the keys in the map.
|
||||
*/
|
||||
*keys(): IterableIterator<K> {
|
||||
for (const bucket of this.buckets) {
|
||||
let node = bucket ?? null;
|
||||
while (node !== null) {
|
||||
yield node.key;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the values in the map.
|
||||
*/
|
||||
*values(): IterableIterator<V> {
|
||||
for (const bucket of this.buckets) {
|
||||
let node = bucket ?? null;
|
||||
while (node !== null) {
|
||||
yield node.value;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the key-value pairs in the map.
|
||||
*/
|
||||
*entries(): IterableIterator<[K, V]> {
|
||||
for (const bucket of this.buckets) {
|
||||
let node = bucket ?? null;
|
||||
while (node !== null) {
|
||||
yield [node.key, node.value];
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the HashMap iterable (for...of loops).
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a callback for each key-value pair in the map.
|
||||
*/
|
||||
forEach(callback: (value: V, key: K, map: IHashMap<K, V>) => void): void {
|
||||
for (const [key, value] of this.entries()) {
|
||||
callback(value, key, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the map.
|
||||
*/
|
||||
toString(): string {
|
||||
const entries: string[] = [];
|
||||
for (const [key, value] of this.entries()) {
|
||||
entries.push(`${String(key)} => ${String(value)}`);
|
||||
}
|
||||
return `HashMap(${this._size}) { ${entries.join(", ")} }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two keys are equal.
|
||||
* Handles primitive types and object references.
|
||||
*/
|
||||
private keysEqual(key1: K, key2: K): boolean {
|
||||
// Handle NaN case
|
||||
if (typeof key1 === "number" && typeof key2 === "number") {
|
||||
if (Number.isNaN(key1) && Number.isNaN(key2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard equality
|
||||
return key1 === key2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the map has sufficient capacity.
|
||||
* Resizes if load factor exceeds threshold.
|
||||
*/
|
||||
private ensureCapacity(): void {
|
||||
if (this.loadFactor >= this.loadFactorThreshold) {
|
||||
this.resize(this.buckets.length * 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the hash table to the new capacity.
|
||||
* Rehashes all existing entries.
|
||||
*/
|
||||
private resize(newCapacity: number): void {
|
||||
const oldBuckets = this.buckets;
|
||||
this.buckets = new Array(newCapacity).fill(null);
|
||||
this._size = 0;
|
||||
|
||||
// Rehash all existing entries
|
||||
for (const bucket of oldBuckets) {
|
||||
let node = bucket ?? null;
|
||||
while (node !== null) {
|
||||
this.set(node.key, node.value);
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
src/examples/basic-usage.ts
Normal file
88
src/examples/basic-usage.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { HashMap } from "../index.ts";
|
||||
|
||||
/**
|
||||
* Basic usage examples for the HashMap implementation.
|
||||
*/
|
||||
|
||||
console.log("=== Basic HashMap Usage Examples ===\n");
|
||||
|
||||
// Example 1: String keys with number values
|
||||
console.log("1. String keys with number values:");
|
||||
const scores = new HashMap<string, number>();
|
||||
scores.set("Alice", 95);
|
||||
scores.set("Bob", 87);
|
||||
scores.set("Charlie", 92);
|
||||
|
||||
console.log(`Alice's score: ${scores.get("Alice")}`); // 95
|
||||
console.log(`Map size: ${scores.size}`); // 3
|
||||
console.log(`Has Bob? ${scores.has("Bob")}`); // true
|
||||
console.log("");
|
||||
|
||||
// Example 2: Iterating over entries
|
||||
console.log("2. Iterating with for...of:");
|
||||
for (const [name, score] of scores) {
|
||||
console.log(` ${name}: ${score}`);
|
||||
}
|
||||
console.log("");
|
||||
|
||||
// Example 3: Using forEach
|
||||
console.log("3. Using forEach:");
|
||||
scores.forEach((score, name) => {
|
||||
console.log(` ${name} scored ${score}`);
|
||||
});
|
||||
console.log("");
|
||||
|
||||
// Example 4: Working with keys and values
|
||||
console.log("4. Keys and values:");
|
||||
console.log(" Keys:", Array.from(scores.keys()).join(", "));
|
||||
console.log(" Values:", Array.from(scores.values()).join(", "));
|
||||
console.log("");
|
||||
|
||||
// Example 5: Deleting entries
|
||||
console.log("5. Deleting entries:");
|
||||
console.log(` Deleted Bob? ${scores.delete("Bob")}`); // true
|
||||
console.log(` Deleted David? ${scores.delete("David")}`); // false
|
||||
console.log(` Map size after deletion: ${scores.size}`); // 2
|
||||
console.log("");
|
||||
|
||||
// Example 6: Complex object values
|
||||
console.log("6. Complex object values:");
|
||||
interface User {
|
||||
name: string;
|
||||
email: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
const users = new HashMap<number, User>();
|
||||
users.set(1, { name: "Alice", email: "alice@example.com", age: 30 });
|
||||
users.set(2, { name: "Bob", email: "bob@example.com", age: 25 });
|
||||
|
||||
const user1 = users.get(1);
|
||||
console.log(` User 1: ${user1?.name} (${user1?.email})`);
|
||||
console.log("");
|
||||
|
||||
// Example 7: Clearing the map
|
||||
console.log("7. Clearing the map:");
|
||||
console.log(` Size before clear: ${scores.size}`);
|
||||
scores.clear();
|
||||
console.log(` Size after clear: ${scores.size}`);
|
||||
console.log("");
|
||||
|
||||
// Example 8: Load factor and capacity
|
||||
console.log("8. HashMap internals:");
|
||||
const map = new HashMap<string, number>(4, 0.75); // Small initial capacity
|
||||
console.log(` Initial capacity: ${map.capacity}`);
|
||||
console.log(` Initial load factor: ${map.loadFactor.toFixed(2)}`);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
map.set(`key${i}`, i);
|
||||
}
|
||||
|
||||
console.log(` After 10 inserts:`);
|
||||
console.log(` Size: ${map.size}`);
|
||||
console.log(` Capacity: ${map.capacity}`);
|
||||
console.log(` Load factor: ${map.loadFactor.toFixed(2)}`);
|
||||
console.log("");
|
||||
|
||||
console.log("=== Examples Complete ===");
|
||||
|
||||
111
src/examples/custom-hash-function.ts
Normal file
111
src/examples/custom-hash-function.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { HashMap, NumericHashFunction } from "../index.ts";
|
||||
import type { IHashFunction } from "../index.ts";
|
||||
|
||||
/**
|
||||
* Examples demonstrating custom hash function usage.
|
||||
* This showcases the Dependency Inversion Principle and Open/Closed Principle.
|
||||
*/
|
||||
|
||||
console.log("=== Custom Hash Function Examples ===\n");
|
||||
|
||||
// Example 1: Using the NumericHashFunction for better numeric key distribution
|
||||
console.log("1. Using NumericHashFunction:");
|
||||
const numericMap = new HashMap<number, string>(
|
||||
16,
|
||||
0.75,
|
||||
new NumericHashFunction()
|
||||
);
|
||||
|
||||
numericMap.set(12345, "value1");
|
||||
numericMap.set(67890, "value2");
|
||||
numericMap.set(11111, "value3");
|
||||
|
||||
console.log(` Get 12345: ${numericMap.get(12345)}`);
|
||||
console.log(` Map size: ${numericMap.size}`);
|
||||
console.log("");
|
||||
|
||||
// Example 2: Creating a custom hash function for case-insensitive string keys
|
||||
console.log("2. Case-insensitive string hash function:");
|
||||
|
||||
class CaseInsensitiveHashFunction implements IHashFunction<string> {
|
||||
hash(key: string, capacity: number): number {
|
||||
const lowerKey = key.toLowerCase();
|
||||
let hash = 0;
|
||||
|
||||
for (let i = 0; i < lowerKey.length; i++) {
|
||||
const char = lowerKey.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash;
|
||||
}
|
||||
|
||||
return Math.abs(hash) % capacity;
|
||||
}
|
||||
}
|
||||
|
||||
const caseInsensitiveMap = new HashMap<string, number>(
|
||||
16,
|
||||
0.75,
|
||||
new CaseInsensitiveHashFunction()
|
||||
);
|
||||
|
||||
caseInsensitiveMap.set("Hello", 1);
|
||||
caseInsensitiveMap.set("HELLO", 2); // This will overwrite the previous value
|
||||
|
||||
console.log(` Get "Hello": ${caseInsensitiveMap.get("Hello")}`); // 2
|
||||
console.log(` Get "hello": ${caseInsensitiveMap.get("hello")}`); // 2
|
||||
console.log(` Get "HELLO": ${caseInsensitiveMap.get("HELLO")}`); // 2
|
||||
console.log(` Map size: ${caseInsensitiveMap.size}`); // 1 (keys are treated as equal)
|
||||
console.log("");
|
||||
|
||||
// Example 3: Custom hash function for complex objects
|
||||
console.log("3. Custom hash for complex objects:");
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
class PointHashFunction implements IHashFunction<Point> {
|
||||
hash(key: Point, capacity: number): number {
|
||||
// Cantor pairing function for combining two numbers
|
||||
const hash = ((key.x + key.y) * (key.x + key.y + 1)) / 2 + key.y;
|
||||
return Math.abs(Math.floor(hash)) % capacity;
|
||||
}
|
||||
}
|
||||
|
||||
const pointMap = new HashMap<Point, string>(16, 0.75, new PointHashFunction());
|
||||
|
||||
const p1: Point = { x: 10, y: 20 };
|
||||
const p2: Point = { x: 10, y: 20 }; // Same values but different object
|
||||
const p3: Point = { x: 30, y: 40 };
|
||||
|
||||
pointMap.set(p1, "Point 1");
|
||||
pointMap.set(p3, "Point 3");
|
||||
|
||||
console.log(` Get p1: ${pointMap.get(p1)}`); // "Point 1"
|
||||
console.log(` Get p2 (same values): ${pointMap.get(p2)}`); // undefined (different reference)
|
||||
console.log(` Get p3: ${pointMap.get(p3)}`); // "Point 3"
|
||||
console.log("");
|
||||
|
||||
// Example 4: Modulo-based hash function
|
||||
console.log("4. Simple modulo hash function:");
|
||||
|
||||
class ModuloHashFunction implements IHashFunction<number> {
|
||||
hash(key: number, capacity: number): number {
|
||||
return Math.abs(Math.floor(key)) % capacity;
|
||||
}
|
||||
}
|
||||
|
||||
const moduloMap = new HashMap<number, string>(8, 0.75, new ModuloHashFunction());
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
moduloMap.set(i, `value-${i}`);
|
||||
}
|
||||
|
||||
console.log(` Map size: ${moduloMap.size}`);
|
||||
console.log(` Get 5: ${moduloMap.get(5)}`);
|
||||
console.log(` Get 15: ${moduloMap.get(15)}`);
|
||||
console.log("");
|
||||
|
||||
console.log("=== Custom Hash Function Examples Complete ===");
|
||||
|
||||
43
src/hash-functions/DefaultHashFunction.ts
Normal file
43
src/hash-functions/DefaultHashFunction.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
||||
|
||||
/**
|
||||
* Default hash function implementation.
|
||||
* Follows Single Responsibility Principle (SRP) - only responsible for hashing.
|
||||
* Uses a simple but effective string-based hashing algorithm.
|
||||
*/
|
||||
export class DefaultHashFunction<K> implements IHashFunction<K> {
|
||||
hash(key: K, capacity: number): number {
|
||||
const str = this.convertToString(key);
|
||||
let hash = 0;
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32-bit integer
|
||||
}
|
||||
|
||||
// Ensure positive index
|
||||
return Math.abs(hash) % capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts any key type to a string representation.
|
||||
* @param key - The key to convert
|
||||
* @returns A string representation of the key
|
||||
*/
|
||||
private convertToString(key: K): string {
|
||||
if (key === null) return "null";
|
||||
if (key === undefined) return "undefined";
|
||||
if (typeof key === "string") return key;
|
||||
if (typeof key === "number" || typeof key === "boolean") return String(key);
|
||||
if (typeof key === "object") {
|
||||
try {
|
||||
return JSON.stringify(key);
|
||||
} catch {
|
||||
return Object.prototype.toString.call(key);
|
||||
}
|
||||
}
|
||||
return String(key);
|
||||
}
|
||||
}
|
||||
|
||||
25
src/hash-functions/NumericHashFunction.ts
Normal file
25
src/hash-functions/NumericHashFunction.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { IHashFunction } from "../interfaces/IHashFunction.ts";
|
||||
|
||||
/**
|
||||
* Specialized hash function for numeric keys.
|
||||
* Follows Single Responsibility Principle (SRP).
|
||||
* Provides better distribution for numeric keys than the default hash function.
|
||||
*/
|
||||
export class NumericHashFunction implements IHashFunction<number> {
|
||||
/**
|
||||
* Uses multiplication method for hashing numbers.
|
||||
* This method provides good distribution for numeric keys.
|
||||
*/
|
||||
hash(key: number, capacity: number): number {
|
||||
// Handle special cases
|
||||
if (!Number.isFinite(key)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Multiplication method with golden ratio
|
||||
const A = 0.6180339887; // (√5 - 1) / 2
|
||||
const fractionalPart = (Math.abs(key) * A) % 1;
|
||||
return Math.floor(capacity * fractionalPart);
|
||||
}
|
||||
}
|
||||
|
||||
33
src/index.ts
Normal file
33
src/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @techniker-me/hash-map
|
||||
* A robust HashMap implementation following OOP SOLID principles.
|
||||
*
|
||||
* Features:
|
||||
* - Generic type support for keys and values
|
||||
* - Separate chaining for collision resolution
|
||||
* - Automatic resizing based on load factor
|
||||
* - Custom hash function support
|
||||
* - Full iterator support (keys, values, entries)
|
||||
* - Compatible with for...of loops
|
||||
*
|
||||
* SOLID Principles Applied:
|
||||
* - Single Responsibility: Each class has one clear purpose
|
||||
* - Open/Closed: Extensible through custom hash functions
|
||||
* - Liskov Substitution: All implementations follow their interfaces
|
||||
* - Interface Segregation: Focused, minimal interfaces
|
||||
* - Dependency Inversion: Depends on abstractions (IHashFunction, IHashMap)
|
||||
*/
|
||||
|
||||
// Core implementation
|
||||
export { HashMap } from "./core/HashMap.ts";
|
||||
|
||||
// Interfaces
|
||||
export type { IHashMap } from "./interfaces/IHashMap.ts";
|
||||
export type { IHashFunction } from "./interfaces/IHashFunction.ts";
|
||||
|
||||
// Models
|
||||
export { HashNode } from "./models/HashNode.ts";
|
||||
|
||||
// Hash functions
|
||||
export { DefaultHashFunction } from "./hash-functions/DefaultHashFunction.ts";
|
||||
export { NumericHashFunction } from "./hash-functions/NumericHashFunction.ts";
|
||||
15
src/interfaces/IHashFunction.ts
Normal file
15
src/interfaces/IHashFunction.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Interface for hash functions.
|
||||
* Follows Interface Segregation Principle (ISP) and Dependency Inversion Principle (DIP).
|
||||
* Allows different hashing strategies to be implemented and injected.
|
||||
*/
|
||||
export interface IHashFunction<K> {
|
||||
/**
|
||||
* Computes a hash value for the given key.
|
||||
* @param key - The key to hash
|
||||
* @param capacity - The current capacity of the hash table
|
||||
* @returns A hash value in the range [0, capacity)
|
||||
*/
|
||||
hash(key: K, capacity: number): number;
|
||||
}
|
||||
|
||||
66
src/interfaces/IHashMap.ts
Normal file
66
src/interfaces/IHashMap.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Interface for HashMap operations.
|
||||
* Follows Interface Segregation Principle (ISP).
|
||||
* Defines the contract that all HashMap implementations must follow.
|
||||
*/
|
||||
export interface IHashMap<K, V> {
|
||||
/**
|
||||
* Inserts or updates a key-value pair in the map.
|
||||
* @param key - The key to insert/update
|
||||
* @param value - The value to associate with the key
|
||||
*/
|
||||
set(key: K, value: V): void;
|
||||
|
||||
/**
|
||||
* Retrieves the value associated with the given key.
|
||||
* @param key - The key to look up
|
||||
* @returns The value if found, undefined otherwise
|
||||
*/
|
||||
get(key: K): V | undefined;
|
||||
|
||||
/**
|
||||
* Checks if a key exists in the map.
|
||||
* @param key - The key to check
|
||||
* @returns true if the key exists, false otherwise
|
||||
*/
|
||||
has(key: K): boolean;
|
||||
|
||||
/**
|
||||
* Removes a key-value pair from the map.
|
||||
* @param key - The key to remove
|
||||
* @returns true if the key was found and removed, false otherwise
|
||||
*/
|
||||
delete(key: K): boolean;
|
||||
|
||||
/**
|
||||
* Removes all key-value pairs from the map.
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* Gets the number of key-value pairs in the map.
|
||||
*/
|
||||
get size(): number;
|
||||
|
||||
/**
|
||||
* Returns an iterator over the keys in the map.
|
||||
*/
|
||||
keys(): IterableIterator<K>;
|
||||
|
||||
/**
|
||||
* Returns an iterator over the values in the map.
|
||||
*/
|
||||
values(): IterableIterator<V>;
|
||||
|
||||
/**
|
||||
* Returns an iterator over the key-value pairs in the map.
|
||||
*/
|
||||
entries(): IterableIterator<[K, V]>;
|
||||
|
||||
/**
|
||||
* Executes a callback for each key-value pair in the map.
|
||||
* @param callback - The function to execute for each entry
|
||||
*/
|
||||
forEach(callback: (value: V, key: K, map: IHashMap<K, V>) => void): void;
|
||||
}
|
||||
|
||||
14
src/models/HashNode.ts
Normal file
14
src/models/HashNode.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Represents a node in the hash table's collision chain.
|
||||
* Follows Single Responsibility Principle (SRP) - only responsible for storing key-value pairs.
|
||||
* @template K - The type of the key
|
||||
* @template V - The type of the value
|
||||
*/
|
||||
export class HashNode<K, V> {
|
||||
constructor(
|
||||
public key: K,
|
||||
public value: V,
|
||||
public next: HashNode<K, V> | null = null
|
||||
) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user