262 lines
6.5 KiB
Markdown
262 lines
6.5 KiB
Markdown
# @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
|
|
|
|
```bash
|
|
bun add @techniker-me/hash-map
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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 operations
|
|
- `HashNode` - Stores key-value pairs
|
|
- `DefaultHashFunction` - 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 operations
|
|
- `IHashFunction` - Minimal hashing interface
|
|
- Clients depend only on interfaces they use
|
|
|
|
### 5. Dependency Inversion Principle (DIP)
|
|
- Depends on `IHashFunction` abstraction, not concrete implementations
|
|
- High-level modules don't depend on low-level modules
|
|
|
|
## Architecture
|
|
|
|
```text
|
|
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
|
|
|
|
```bash
|
|
# Basic usage examples
|
|
bun run src/examples/basic-usage.ts
|
|
|
|
# Custom hash function examples
|
|
bun run src/examples/custom-hash-function.ts
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
# 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](TESTING.md) for detailed test documentation
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# 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](https://bun.sh)
|