7.0 KiB
@techniker-me/hash-map
A robust, production-ready HashMap implementation in TypeScript following OOP SOLID principles and best practices.
Features
✨ Generic Type Support - Fully typed keys and values with TypeScript generics
🔧 Custom Hash Functions - Inject your own hashing strategies
⚡ Automatic Resizing - Dynamic capacity adjustment based on load factor
🔗 Collision Resolution - Separate chaining for handling hash collisions
🔄 Full Iterator Support - Compatible with for...of, keys(), values(), entries(), and forEach()
📦 Zero Dependencies - Lightweight and self-contained
🎯 SOLID Principles - Clean, maintainable, and extensible architecture
✅ 100% Test Coverage - 66 comprehensive tests with 100% line coverage using Bun test runner
Installation
bun add @techniker-me/hash-map
Quick Start
import { HashMap } from "@techniker-me/hash-map";
// Create a new HashMap
const map = new HashMap<string, number>();
// Add entries
map.set("Alice", 95);
map.set("Bob", 87);
map.set("Charlie", 92);
// Retrieve values
console.log(map.get("Alice")); // 95
// Check existence
console.log(map.has("Bob")); // true
// Delete entries
map.delete("Bob");
// Iterate over entries
for (const [name, score] of map) {
console.log(`${name}: ${score}`);
}
// Get size
console.log(map.size); // 2
API Reference
Constructor
new HashMap<K, V>(
initialCapacity?: number, // Default: 16
loadFactorThreshold?: number, // Default: 0.75
hashFunction?: IHashFunction<K> // Default: DefaultHashFunction
)
Methods
| Method | Description | Time Complexity |
|---|---|---|
set(key: K, value: V): void |
Insert or update a key-value pair | O(1) average |
get(key: K): V | undefined |
Retrieve value by key | O(1) average |
has(key: K): boolean |
Check if key exists | O(1) average |
delete(key: K): boolean |
Remove entry by key | O(1) average |
clear(): void |
Remove all entries | O(n) |
keys(): IterableIterator<K> |
Iterator over keys | O(n) |
values(): IterableIterator<V> |
Iterator over values | O(n) |
entries(): IterableIterator<[K, V]> |
Iterator over entries | O(n) |
forEach(callback): void |
Execute callback for each entry | O(n) |
Properties
| Property | Description |
|---|---|
size |
Number of key-value pairs |
capacity |
Current number of buckets |
loadFactor |
Current load factor (size / capacity) |
Advanced Usage
Custom Hash Functions
Implement the IHashFunction<K> interface to create custom hashing strategies:
import { HashMap, IHashFunction } from "@techniker-me/hash-map";
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++) {
hash = (hash << 5) - hash + lowerKey.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash) % capacity;
}
}
const map = new HashMap<string, number>(
16,
0.75,
new CaseInsensitiveHashFunction(),
);
map.set("Hello", 1);
console.log(map.get("HELLO")); // 1 (case-insensitive)
Numeric Keys
Use the built-in NumericHashFunction for better distribution with numeric keys:
import { HashMap, NumericHashFunction } from "@techniker-me/hash-map";
const map = new HashMap<number, string>(16, 0.75, new NumericHashFunction());
map.set(12345, "value1");
map.set(67890, "value2");
Working with Complex Types
interface User {
id: number;
name: string;
email: string;
}
const users = new HashMap<number, User>();
users.set(1, { id: 1, name: "Alice", email: "alice@example.com" });
users.set(2, { id: 2, name: "Bob", email: "bob@example.com" });
const user = users.get(1);
console.log(user?.name); // "Alice"
SOLID Principles
This implementation adheres to all five SOLID principles:
1. Single Responsibility Principle (SRP)
HashMap- Manages hash map operationsHashNode- Stores key-value pairsDefaultHashFunction- Handles hashing logic- Each class has one clear purpose
2. Open/Closed Principle (OCP)
- Extensible through custom hash functions
- Core implementation is closed for modification
3. Liskov Substitution Principle (LSP)
- All implementations correctly implement their interfaces
- Subtypes can replace their base types without breaking functionality
4. Interface Segregation Principle (ISP)
IHashMap- Focused map operationsIHashFunction- Minimal hashing interface- Clients depend only on interfaces they use
5. Dependency Inversion Principle (DIP)
- Depends on
IHashFunctionabstraction, not concrete implementations - High-level modules don't depend on low-level modules
Architecture
src/
├── core/
│ └── HashMap.ts # Main HashMap implementation
├── interfaces/
│ ├── IHashMap.ts # HashMap interface
│ └── IHashFunction.ts # Hash function interface
├── models/
│ └── HashNode.ts # Node for collision chains
├── hash-functions/
│ ├── DefaultHashFunction.ts # Default hashing strategy
│ └── NumericHashFunction.ts # Numeric key optimization
├── examples/
│ ├── basic-usage.ts # Basic usage examples
│ └── custom-hash-function.ts # Advanced examples
└── index.ts # Public API exports
Performance
- Average Case: O(1) for all basic operations (get, set, has, delete)
- Worst Case: O(n) when all keys collide (very rare with good hash functions)
- Space Complexity: O(n) where n is the number of entries
- Automatic Resizing: Triggers when load factor exceeds threshold (default 0.75)
Examples
Running Examples
# Basic usage examples
bun run src/examples/basic-usage.ts
# Custom hash function examples
bun run src/examples/custom-hash-function.ts
Testing
# Run all tests
bun test
# Run tests in watch mode
bun test --watch
# Run tests with coverage report
bun test --coverage
Test Coverage: 100% ✅
- 66 comprehensive tests
- 1,168 assertions
- All edge cases covered
- See TESTING.md for detailed test documentation
Development
# Install dependencies
bun install
# Run tests
bun test
# Run examples
bun run src/examples/basic-usage.ts
License
MIT
Author
Techniker.me
Built with ❤️ using Bun