https://github.com/bluuewhale/hash-smith
Fast & memory efficient hash tables for Java
https://github.com/bluuewhale/hash-smith
benchmark collections hashtable java jmh open-addressing robinhood-hashing simd swiss-table
Last synced: about 11 hours ago
JSON representation
Fast & memory efficient hash tables for Java
- Host: GitHub
- URL: https://github.com/bluuewhale/hash-smith
- Owner: bluuewhale
- License: mit
- Created: 2025-12-05T15:52:13.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-04-05T02:43:52.000Z (3 months ago)
- Last Synced: 2026-04-05T03:12:46.330Z (3 months ago)
- Topics: benchmark, collections, hashtable, java, jmh, open-addressing, robinhood-hashing, simd, swiss-table
- Language: Java
- Homepage:
- Size: 4.02 MB
- Stars: 96
- Watchers: 0
- Forks: 7
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-java - HashSmith - Hash map and set implementations using SwissTable-style SWAR/SIMD control-byte probing, optimized for memory efficiency. (Projects / Data Structures)
- awesome-java - HashSmith - Hash map and set implementations using SwissTable-style SWAR/SIMD control-byte probing, optimized for memory efficiency. (Projects / Data Structures)
README
# HashSmith: Fast & memory efficient hash tables for Java
[](LICENSE)
[](https://central.sonatype.com/artifact/io.github.bluuewhale/hashsmith)
[](https://javadoc.io/doc/io.github.bluuewhale/hashsmith)
[](#overview)
## Overview
- HashSmith provides multiple high-performance hash table implementations optimized for speed and memory efficiency on modern JVMs.
- Focus areas: SWAR-probing (`SwissMap`), SIMD-probing (`SwissSimdMap`), predictable probe lengths (Robin Hood), and minimal per-entry overhead.
- Built for JDK 21+; `SwissSimdMap` uses the incubating Vector API for SIMD acceleration.
- More memory-efficient than the built-in JDK `HashMap`; performance depends on workload.
## Implementations
- **SwissMap**: SwissTable-inspired design using SWAR control-byte probing (no Vector API) with tombstone reuse. Default map.
- **SwissSimdMap**: SIMD (Vector API incubator) variant of SwissMap with vectorized control-byte probing. See `docs/SwissSimdMap.md` for details.
- **ConcurrentSwissMap**: sharded, thread-safe wrapper around `SwissMap` using per-shard `StampedLock` (null keys not supported).
- **SwissSet**: SwissTable-style hash set with SIMD control-byte probing, tombstone reuse, and null-element support
### Why SWAR by default?
Vector API is still incubating, and profiling on my setup showed the SIMD path taking longer than expected, so the default `SwissMap` favors a SWAR probe. Numbers can differ significantly by hardware/JVM version; please run your own benchmarks if you plan to use `SwissSimdMap`.
## Blog / Write-up
- If you want a guided tour with design notes and benchmarks, see **[this write-up](https://bluuewhale.github.io/posts/building-a-fast-and-memory-efficient-hash-table-in-java-by-borrowing-the-best-ideas/)**.
## Quick Start
```java
import io.github.bluuewhale.hashsmith.SwissMap; // SWAR
import io.github.bluuewhale.hashsmith.ConcurrentSwissMap;
import io.github.bluuewhale.hashsmith.SwissSet;
// SwissMap (SWAR)
var swiss = new SwissMap();
swiss.put("a", 1);
swiss.get("a"); // 1
// ConcurrentSwissMap (sharded, thread-safe)
var concurrentSwiss = new ConcurrentSwissMap();
concurrentSwiss.put("a", 1);
concurrentSwiss.get("a"); // 1
// SwissSet
var swissSet = new SwissSet();
swissSet.add("k");
swissSet.add(null); // nulls allowed
swissSet.contains("k"); // true
```
## Install
- Gradle (Kotlin DSL):
```kotlin
dependencies {
implementation("io.github.bluuewhale:hashsmith:0.2.0")
}
```
- Gradle (Groovy):
```groovy
dependencies {
implementation 'io.github.bluuewhale:hashsmith:0.2.0'
}
```
- Maven:
```xml
io.github.bluuewhale
hashsmith
0.2.0
```
## Requirements
- JDK 21+ (`SwissSimdMap` needs `jdk.incubator.vector`)
- Gradle (wrapper provided)
- The JVM flag `--add-modules jdk.incubator.vector` is already configured for build, test, and JMH tasks that exercise `SwissSimdMap`.
## Build & Test
```bash
./gradlew build # full build
./gradlew test # JUnit 5 tests
```
## Memory Footprint
- Compares retained heap for both maps (`HashMap` vs `SwissSimdMap` vs `SwissMap` vs fastutil `Object2ObjectOpenHashMap` vs Eclipse Collections `UnifiedMap`) and sets (`HashSet` vs `SwissSet` vs fastutil `ObjectOpenHashSet` vs Eclipse Collections `UnifiedSet`).
- Set benchmarks use UUID `String` keys (HashSet, SwissSet, ObjectOpenHashSet, UnifiedSet). Primitive-specialized collections (e.g., fastutil primitive sets) are excluded because their memory profile is driven by primitive storage, whereas these tests target general reference workloads.
### Results
- Maps: `SwissMap`/`SwissSimdMap` use open addressing to cut space; default load factor 0.875, up to 53.3% retained-heap reduction in payload-light cases vs `HashMap`.
- Sets: `SwissSet` (SwissHashSet) mirrors the SwissTable layout with SIMD control-byte probing and reuses tombstones to stay denser than `HashSet` across tested payloads, showing up to ~62% retained-heap reduction in lighter payload cases.
Map
Set
## Benchmark (JMH, CPU ns/op)
- All benchmarks run on the same machine: Eclipse Temurin JDK 21, macOS/Apple Silicon, `AverageTime` mode, 3 forks.
- SwissMap numbers reflect exp-001 optimizations.
| put hit | put miss |
|---------------------------------------------|-----------------------------------------------|
|  |  |
| get hit | get miss |
| --- | --- |
|  |  |
## Contributing
1) Open an issue for bugs/ideas
2) Work on a feature branch and open a PR
3) Keep tests/JMH green before submitting
## License
- This project is licensed under the MIT License. See [`LICENSE`](./LICENSE) for details.