# hashmap-concurrenthashmap Companion code for the ankurm.com post *"Java HashMap vs ConcurrentHashMap: Complete Interview Guide."* Ninth module in `java-core-examples`. ## Versions this was built and tested against | Component | Version | Notes | |---|---|---| | JDK | 25.0.4.1+1 (Temurin, LTS) | | | JMH | 1.37 | Throughput mode, 3x1s warmup, 5x1s measurement, 1 fork. | | JUnit Jupiter | 5.11.0 | | | Maven | 3.9.11 | | | Hardware | 2 vCPU x86-64 VM | Same sandbox as the rest of this series - see the benchmark's own javadoc for what that does and doesn't tell you at higher thread counts. | ## Quickstart ```bash export JAVA_HOME=/path/to/jdk-25 mvn package java -cp target/classes com.ankurm.hashmapconcurrenthashmap.ComputeIfAbsentRecursionDemo java -cp target/classes com.ankurm.hashmapconcurrenthashmap.NullHandlingDemo java -jar target/benchmarks.jar PutGetBenchmark -t 4 ``` `scripts/run-all.sh` regenerates every file in `output/` (needs `JDK25_HOME`). ## What's in here | File | What it shows | |---|---| | `.../ComputeIfAbsentRecursionDemo.java` | Three real outcomes of recursing into the same map from inside `computeIfAbsent`: `ConcurrentHashMap` tolerates it for a different key, `HashMap` throws `ConcurrentModificationException` for a different key even single-threaded, and `ConcurrentHashMap` throws `IllegalStateException: Recursive update` for the same key. | | `.../NullHandlingDemo.java` | `HashMap` allows one null key and any number of null values; `ConcurrentHashMap` rejects null keys and values on `put()` - and rejects a null key on `get()` too. | | `.../PutGetBenchmark.java` | JMH: an 80% get / 20% put workload against `Collections.synchronizedMap(new HashMap<>())` vs `ConcurrentHashMap`, swept across 1/2/4/8 threads. | | `src/test/.../MapClaimsTest.java` | Asserts every claim above. | | `output/01`-`02` | Each trap demo's real run. | | `output/03`-`06` | The benchmark sweep, one file per thread count. | | `output/07` | JUnit correctness run. | ## Reading the results honestly (2-vCPU sandbox) `ConcurrentHashMap` beat the synchronized wrapper even at 1 thread (`output/03`) - every call into `synchronizedMap` pays for entering and exiting a monitor whether or not another thread is waiting, while `ConcurrentHashMap`'s reads (80% of this workload) are lock-free from the start. The gap widens sharply from 2 threads on (`output/04`-`06`): the synchronized wrapper's single monitor means every thread, including readers, queues behind whichever thread currently holds it, while `ConcurrentHashMap` only locks the bucket a write touches and never locks reads at all. On this 2-vCPU sandbox, throughput for both flattens once thread count exceeds the core count - that's the hardware's ceiling, not evidence either map "stops scaling" at some inherent limit. ## License MIT - see the [repo-wide LICENSE](../LICENSE).