Wormhole4j is a high-performance sorted map for Java 8 or later, supporting thread-safe concurrent access, based on the research paper "Wormhole: A Fast Ordered Index for In-memory Data Management".
- Supports
put(),get(),scan(),scanWithCount(),snapshotScan(), anddelete()operations for Integer, Long, and String keys - Significantly faster
scan()API for range scans (inclusive/exclusive) — 2×–3× faster than Red-Black trees, 6×–8× faster than AVL trees - Excellent performance for String keys — about 40% faster get/update operations than tree-based alternatives
- Thread-safe concurrent access via
setConcurrent(true)in the builder — outperformsConcurrentSkipListMapfor update+get and String key scans; trade-off on concurrent numeric key scans
Add the following to your pom.xml:
<dependency>
<groupId>org.komamitsu</groupId>
<artifactId>wormhole4j</artifactId>
<version>0.3.1</version>
</dependency>Add the following to your build.gradle:
implementation 'org.komamitsu:wormhole4j:0.3.1'Add the following to your build.gradle.kts:
implementation("org.komamitsu:wormhole4j:0.3.1")// Example with String keys
Wormhole<String, String> wormholeStr = new WormholeBuilder.ForStringKey<String>().build();
String existingValue = wormholeStr.put("James", "semaj"); // returns null
wormholeStr.put("Joseph", "hpesoj");
wormholeStr.put("John", "nhoj");
wormholeStr.put("Jacob", "bocaj");
wormholeStr.put("Jason", "nosaj");
String value = wormholeStr.get("James"); // returns "semaj"
// Scan up to 3 entries starting from "Ja"
List<KeyValue<String, String>> scanResult = wormholeStr.scanWithCount("Ja", 3);
// Range scan (exclusive end) with a callback
wormholeStr.scan("Ja", "Joseph", true, (k, v) -> {
/*
if (k.equals("unexpected_key")) {
// If you want to stop the scan, return false.
return false;
}
*/
return true;
});
// Range scan (exclusive end) returning a list
List<KeyValue<String, String>> rangeScanResult = wormholeStr.scan("Ja", "Joseph", true);
// Delete a record
String deletedValue = wormholeStr.delete("James"); // returns "semaj"
// Example with Integer keys
Wormhole<Integer, String> wormholeInt = new WormholeBuilder.ForIntKey<String>().build();
wormholeInt.put(100, "hundred");
// Example with Long keys
Wormhole<Long, String> wormholeLong = new WormholeBuilder.ForLongKey<String>().build();
wormholeLong.put(9000000000L, "nine billion");When using Wormhole from multiple threads, enable concurrent mode via the builder.
Each thread must call registerThread() before accessing the Wormhole and unregisterThread() when done.
Wormhole<String, String> wormhole = new WormholeBuilder.ForStringKey<String>()
.setConcurrent(true)
.build();
ExecutorService executor = Executors.newFixedThreadPool(2);
try {
// Thread 1
executor.submit(() -> {
wormhole.registerThread();
try {
wormhole.put("James", "semaj");
wormhole.put("Joseph", "hpesoj");
wormhole.put("John", "nhoj");
} finally {
wormhole.unregisterThread();
}
});
// Thread 2
executor.submit(() -> {
wormhole.registerThread();
try {
String value = wormhole.get("James"); // returns "semaj" if the put() operation has completed, otherwise null
// Use snapshotScan() for a consistent snapshot across all scanned records
List<KeyValue<String, String>> snapshot = wormhole.snapshotScan("James", "Joseph", true);
} finally {
wormhole.unregisterThread();
}
});
} finally {
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
}The performance of Wormhole4j was evaluated against well-known sorted map implementations.
- Record count: 100,000 records
- Operations measured:
- Get: 10 second operations
- Update: 10 second operations
- Insert: 100,000 operations
- Remove: 100,000 operations
- Scan: 10 second operations (range scans with scan size of 512 records)
- Key types tested: Integer, Long, and String
- String keys: length range 32-256 characters
- JVM version: Java 21
java.util.TreeMap- Red-Black tree from Java standard libraryit.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap- AVL tree fromit.unimi.dsi:fastutil:8.5.16
All values are throughput-relative percentages (positive = faster, negative = slower; bold: notable advantage, underlined: disadvantage) — e.g., +9% means 9% higher throughput, +510% means 6.1× the baseline throughput.
vs AVL Tree (Object2ObjectAVLTreeMap)
| Operation | Integer | Long | String |
|---|---|---|---|
| Get | +9% | +9% | +44% |
| Update | +24% | +18% | +38% |
| Insert | -25% | -25% | -13% |
| Scan | +510% | +550% | +724% |
| Remove | -69% | -62% | -54% |
vs Red-Black Tree (java.util.TreeMap)
| Operation | Integer | Long | String |
|---|---|---|---|
| Get | +10% | +14% | +48% |
| Update | +7% | +6% | +40% |
| Insert | -43% | -46% | -12% |
| Scan | +106% | +94% | +167% |
| Remove | -73% | -72% | -68% |
Scan is Wormhole's dominant strength — range traversal benefits from the linked leaf node structure. String key Get and Update show consistent advantages from the hash-table-based trie. Insert and Remove carry trade-offs inherent to the leaf node design.
The concurrent performance of Wormhole4j was evaluated with simultaneous read and write operations across varying thread counts.
- Record count: 100,000 records
- Scenarios: N Update threads and N Get (or Scan) threads running concurrently, where N = 1, 2, 4, 8
- Key types tested: Integer, Long, and String
- String keys: length range 32-256 characters
- JVM version: Java 21
java.util.concurrent.ConcurrentSkipListMap- Concurrent sorted map from Java standard library
Update + Get: Wormhole consistently outperforms ConcurrentSkipListMap across all thread counts and key types. The advantage is largest for String keys (up to 2× faster at 1+1 threads, 56% faster at 8+8) and solid for numeric keys (15–48% faster depending on thread count).
Update + Scan (Update throughput): Similarly strong gains, especially for String keys (up to 2.3× faster Update at 1+1 threads, 86% faster at 8+8).
Update + Scan (Scan throughput): Results are mixed. For String keys, Wormhole is faster across all thread counts (30–89% faster). For Integer and Long keys, ConcurrentSkipListMap has a Scan advantage (38–48% faster) under concurrent write pressure — a trade-off to be aware of for scan-heavy workloads with numeric keys.
Scalability: Both implementations scale roughly linearly from 1+1 to 4+4 threads (around 3× throughput gain) and flatten similarly beyond that. Wormhole's throughput advantage is maintained at all tested thread counts.
- Persistence support – Add an optional persistent variant of Wormhole.
- Further optimization
Apache License 2.0.










