list-benchmarks: ArrayList vs LinkedList JMH benchmarks (add/get/iterate/remove) and when ArrayDeque fits instead of either

This commit is contained in:
Claude
2026-10-01 04:30:17 +00:00
parent 1ac2f6077a
commit 9059df4ad2
14 changed files with 1450 additions and 0 deletions
+1
View File
@@ -20,6 +20,7 @@ article; each module's own README has that article's version table, quickstart,
| [`strings`](strings/) | Java Strings Deep Dive: Interning, StringBuilder, Text Blocks, and the Concatenation Benchmark |
| [`collections`](collections/) | How to Sort a HashMap by Value (and Key) in Java |
| [`nio2`](nio2/) | Java NIO.2 File API: Files, Path, WatchService, and Streaming Large Files Without OOM |
| [`list-benchmarks`](list-benchmarks/) | ArrayList vs LinkedList in 2026: JMH Benchmarks and Why LinkedList Rarely Wins |
## License
+81
View File
@@ -0,0 +1,81 @@
# list-benchmarks
Companion code for the ankurm.com post *"ArrayList vs LinkedList in 2026: JMH Benchmarks and Why
LinkedList Rarely Wins."* Module in `java-core-examples`, the Java-core series.
## Versions this was built and tested against
| Component | Version | Notes |
|---|---|---|
| JDK | 25.0.4.1+1 (Temurin, LTS) | |
| JMH | 1.37 | |
| JUnit Jupiter | 5.11.0 | Correctness tests only - do not read these as throughput proof. |
| Maven | 3.9.11 | |
| Hardware | shared multi-tenant container | See the honesty note below - this is not an isolated benchmarking box. |
## Quickstart
```bash
export JAVA_HOME=/path/to/jdk-25
mvn package
java -jar target/benchmarks.jar ListOpsBenchmark
java -jar target/benchmarks.jar DequeBenchmark
java -cp target/classes com.ankurm.listbenchmarks.IteratorRemovalDemo
```
`scripts/run-all.sh` regenerates every file in `output/`. `scripts/run.sh <Class> [jmh-args]` runs
one benchmark ad hoc.
## What's in here
| File | What it shows |
|---|---|
| `src/main/java/.../ListOpsBenchmark.java` | `ArrayList` vs `LinkedList`: append, front-insert, middle-insert, random `get`, full iteration. Each mutating benchmark inserts then immediately removes, so list size stays constant across the whole run. |
| `src/main/java/.../DequeBenchmark.java` | `ArrayDeque` vs `LinkedList` vs `ArrayList` used as a stack/queue: push/pop at the head, offer/poll at the tail. |
| `src/main/java/.../IteratorRemovalDemo.java` | The one case `LinkedList` can win: removing through an `Iterator` cursor instead of by index. Plain `nanoTime()`, not JMH - read as illustrative. |
| `src/test/java/.../CorrectnessTest.java` | Sanity checks: both lists agree on contents and ordering; `ArrayDeque` genuinely does not implement `List`. |
| `output/01-list-ops-sweep.txt` | Full JMH report for `ListOpsBenchmark`, sizes 1,000 and 100,000. |
| `output/02-deque-ops-sweep.txt` | Full JMH report for `DequeBenchmark`, sizes 1,000 and 100,000. |
| `output/03-iterator-removal-demo.txt` | Captured run of the iterator-removal illustration. |
| `output/04-correctness-test.txt` | JUnit run (4/4 passing). |
## Reading the numbers honestly (shared container, not an isolated box)
This run shares CPU with five other concurrent build/benchmark workloads on the same host - JMH
was still run one class at a time, back to back, never overlapping with itself, but it cannot see
or control what else is scheduled on the machine. Several rows carry an error bar wider than the
mean (`DequeBenchmark.offerPollTail ArrayDeque @1000`: 203,926 &plusmn; 985,600 ops/ms), which is
the signature of scheduler noise, not of the JVM doing something strange. Treat every number here
as **directional**, and prefer the **ratios** between rows measured in the same run over the
absolute ops/ms.
With that caveat:
- **`addFront` is the one operation where `LinkedList` clearly and consistently wins** - flat
~43,000 ops/ms at both 1,000 and 100,000 elements, because it is an O(1) pointer write
regardless of size. `ArrayList.addFront` falls from ~6,600 ops/ms at 1,000 elements to ~53
ops/ms at 100,000 - two orders of magnitude - because every call shifts the whole array by one.
- **`addMiddle` favors `ArrayList` by roughly 20-25x at both sizes**, even though both
implementations are doing O(n) work to get there. `ArrayList`'s half is a single
`System.arraycopy`; `LinkedList`'s is a pointer-chasing walk node by node to find the midpoint,
and that walk is the slower half of "O(n) to reach the middle, O(1) to unlink."
- **`getRandom` and `iterateSum` both favor `ArrayList`**, the first by ~2,000x at 100,000
elements (random access on `LinkedList` is a full traversal per call), the second by ~4-5x even
though iteration is O(n) either way - this is the cache-locality gap the post discusses: one
contiguous `int[]`-backed array versus following a chain of individually-allocated `Node`
objects scattered across the heap.
- **For stack/queue-shaped work, `ArrayDeque` is the one to reach for, not `LinkedList`** -
`pushPopHead` favors `ArrayDeque` over `LinkedList` by ~3.6x at 1,000 elements, and `ArrayDeque`
never degrades to `ArrayList`'s O(n) head-shift problem (`ArrayList.pushPopHead` collapses to 56
ops/ms at 100,000 elements, a 1,800x gap from `ArrayDeque`'s 103,406).
- **The iterator-removal illustration (`output/03`) is the sharpest number in this repository**:
removing every third element of a 200,000-element list through an `Iterator` cursor takes
**1,314 ms with `ArrayList` and 5.0 ms with `LinkedList` - a 262x gap** - because
`ArrayList.remove(index)` still shifts the tail of the array on every call even when the index
came from an iterator already sitting on it, while `LinkedList.Iterator.remove()` unlinks the
node the cursor already holds. This is also, honestly, close to the *only* realistic case where
reaching for `LinkedList` over `ArrayList` is the right call in 2026 code.
## License
MIT - see the [repo-wide LICENSE](../LICENSE).
@@ -0,0 +1,576 @@
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration 1: 162114.256 ops/ms
# Warmup Iteration 2: 163227.532 ops/ms
Iteration 1: 174081.389 ops/ms
Iteration 2: 181016.166 ops/ms
Iteration 3: 178653.294 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
177916.950 ±(99.9%) 64318.989 ops/ms [Average]
(min, avg, max) = (174081.389, 177916.950, 181016.166), stdev = 3525.541
CI (99.9%): [113597.961, 242235.939] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 5.00% complete, ETA 00:01:43
# Fork: 1 of 1
# Warmup Iteration 1: 85814.984 ops/ms
# Warmup Iteration 2: 77594.269 ops/ms
Iteration 1: 84356.568 ops/ms
Iteration 2: 83619.315 ops/ms
Iteration 3: 72410.302 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
80128.728 ±(99.9%) 122132.789 ops/ms [Average]
(min, avg, max) = (72410.302, 80128.728, 84356.568), stdev = 6694.510
CI (99.9%): [≈ 0, 202261.517] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 10.00% complete, ETA 00:01:37
# Fork: 1 of 1
# Warmup Iteration 1: 74571.629 ops/ms
# Warmup Iteration 2: 42845.531 ops/ms
Iteration 1: 44822.469 ops/ms
Iteration 2: 46759.673 ops/ms
Iteration 3: 47030.988 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
46204.377 ±(99.9%) 21973.304 ops/ms [Average]
(min, avg, max) = (44822.469, 46204.377, 47030.988), stdev = 1204.431
CI (99.9%): [24231.073, 68177.680] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 15.00% complete, ETA 00:01:32
# Fork: 1 of 1
# Warmup Iteration 1: 82182.548 ops/ms
# Warmup Iteration 2: 48790.149 ops/ms
Iteration 1: 41766.671 ops/ms
Iteration 2: 42867.682 ops/ms
Iteration 3: 38072.881 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addEnd":
40902.411 ±(99.9%) 45819.499 ops/ms [Average]
(min, avg, max) = (38072.881, 40902.411, 42867.682), stdev = 2511.521
CI (99.9%): [≈ 0, 86721.910] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 20.00% complete, ETA 00:01:26
# Fork: 1 of 1
# Warmup Iteration 1: 5358.049 ops/ms
# Warmup Iteration 2: 5705.109 ops/ms
Iteration 1: 6860.775 ops/ms
Iteration 2: 7014.548 ops/ms
Iteration 3: 5879.974 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
6585.099 ±(99.9%) 11228.597 ops/ms [Average]
(min, avg, max) = (5879.974, 6585.099, 7014.548), stdev = 615.477
CI (99.9%): [≈ 0, 17813.697] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 25.00% complete, ETA 00:01:21
# Fork: 1 of 1
# Warmup Iteration 1: 35.381 ops/ms
# Warmup Iteration 2: 45.342 ops/ms
Iteration 1: 53.127 ops/ms
Iteration 2: 53.514 ops/ms
Iteration 3: 53.857 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
53.499 ±(99.9%) 6.659 ops/ms [Average]
(min, avg, max) = (53.127, 53.499, 53.857), stdev = 0.365
CI (99.9%): [46.840, 60.158] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 30.00% complete, ETA 00:01:16
# Fork: 1 of 1
# Warmup Iteration 1: 79349.690 ops/ms
# Warmup Iteration 2: 46393.515 ops/ms
Iteration 1: 42718.334 ops/ms
Iteration 2: 41957.209 ops/ms
Iteration 3: 44017.076 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
42897.540 ±(99.9%) 19001.947 ops/ms [Average]
(min, avg, max) = (41957.209, 42897.540, 44017.076), stdev = 1041.561
CI (99.9%): [23895.592, 61899.487] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addFront
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 35.00% complete, ETA 00:01:11
# Fork: 1 of 1
# Warmup Iteration 1: 23976.443 ops/ms
# Warmup Iteration 2: 30056.587 ops/ms
Iteration 1: 44990.603 ops/ms
Iteration 2: 44365.967 ops/ms
Iteration 3: 40586.360 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addFront":
43314.310 ±(99.9%) 43475.313 ops/ms [Average]
(min, avg, max) = (40586.360, 43314.310, 44990.603), stdev = 2383.029
CI (99.9%): [≈ 0, 86789.623] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 40.00% complete, ETA 00:01:05
# Fork: 1 of 1
# Warmup Iteration 1: 16685.105 ops/ms
# Warmup Iteration 2: 15174.527 ops/ms
Iteration 1: 15288.973 ops/ms
Iteration 2: 13588.771 ops/ms
Iteration 3: 14183.387 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
14353.710 ±(99.9%) 15740.742 ops/ms [Average]
(min, avg, max) = (13588.771, 14353.710, 15288.973), stdev = 862.803
CI (99.9%): [≈ 0, 30094.453] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 45.00% complete, ETA 00:00:59
# Fork: 1 of 1
# Warmup Iteration 1: 66.502 ops/ms
# Warmup Iteration 2: 108.785 ops/ms
Iteration 1: 127.539 ops/ms
Iteration 2: 124.636 ops/ms
Iteration 3: 124.909 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
125.695 ±(99.9%) 29.247 ops/ms [Average]
(min, avg, max) = (124.636, 125.695, 127.539), stdev = 1.603
CI (99.9%): [96.447, 154.942] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 50.00% complete, ETA 00:00:54
# Fork: 1 of 1
# Warmup Iteration 1: 536.060 ops/ms
# Warmup Iteration 2: 606.294 ops/ms
Iteration 1: 605.980 ops/ms
Iteration 2: 605.421 ops/ms
Iteration 3: 614.996 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
608.799 ±(99.9%) 98.048 ops/ms [Average]
(min, avg, max) = (605.421, 608.799, 614.996), stdev = 5.374
CI (99.9%): [510.751, 706.847] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 55.00% complete, ETA 00:00:49
# Fork: 1 of 1
# Warmup Iteration 1: 4.930 ops/ms
# Warmup Iteration 2: 4.993 ops/ms
Iteration 1: 5.047 ops/ms
Iteration 2: 5.149 ops/ms
Iteration 3: 5.061 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.addMiddle":
5.086 ±(99.9%) 1.014 ops/ms [Average]
(min, avg, max) = (5.047, 5.086, 5.149), stdev = 0.056
CI (99.9%): [4.072, 6.100] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 60.00% complete, ETA 00:00:43
# Fork: 1 of 1
# Warmup Iteration 1: 72989.593 ops/ms
# Warmup Iteration 2: 73568.287 ops/ms
Iteration 1: 74557.252 ops/ms
Iteration 2: 76233.965 ops/ms
Iteration 3: 75917.857 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
75569.691 ±(99.9%) 16253.874 ops/ms [Average]
(min, avg, max) = (74557.252, 75569.691, 76233.965), stdev = 890.930
CI (99.9%): [59315.818, 91823.565] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 65.00% complete, ETA 00:00:37
# Fork: 1 of 1
# Warmup Iteration 1: 39087.453 ops/ms
# Warmup Iteration 2: 42258.929 ops/ms
Iteration 1: 41705.407 ops/ms
Iteration 2: 37824.238 ops/ms
Iteration 3: 41843.150 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
40457.598 ±(99.9%) 41624.817 ops/ms [Average]
(min, avg, max) = (37824.238, 40457.598, 41843.150), stdev = 2281.597
CI (99.9%): [≈ 0, 82082.415] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 70.00% complete, ETA 00:00:32
# Fork: 1 of 1
# Warmup Iteration 1: 2573.620 ops/ms
# Warmup Iteration 2: 2593.939 ops/ms
Iteration 1: 2578.054 ops/ms
Iteration 2: 1819.430 ops/ms
Iteration 3: 2350.190 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
2249.225 ±(99.9%) 7101.544 ops/ms [Average]
(min, avg, max) = (1819.430, 2249.225, 2578.054), stdev = 389.260
CI (99.9%): [≈ 0, 9350.769] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 75.00% complete, ETA 00:00:26
# Fork: 1 of 1
# Warmup Iteration 1: 19.457 ops/ms
# Warmup Iteration 2: 19.681 ops/ms
Iteration 1: 19.396 ops/ms
Iteration 2: 19.827 ops/ms
Iteration 3: 18.270 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.getRandom":
19.165 ±(99.9%) 14.662 ops/ms [Average]
(min, avg, max) = (18.270, 19.165, 19.827), stdev = 0.804
CI (99.9%): [4.503, 33.826] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 80.00% complete, ETA 00:00:21
# Fork: 1 of 1
# Warmup Iteration 1: 2001.245 ops/ms
# Warmup Iteration 2: 1969.018 ops/ms
Iteration 1: 1869.923 ops/ms
Iteration 2: 1798.606 ops/ms
Iteration 3: 1915.867 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
1861.466 ±(99.9%) 1077.953 ops/ms [Average]
(min, avg, max) = (1798.606, 1861.466, 1915.867), stdev = 59.086
CI (99.9%): [783.513, 2939.418] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 85.00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration 1: 13.767 ops/ms
# Warmup Iteration 2: 14.572 ops/ms
Iteration 1: 14.687 ops/ms
Iteration 2: 13.892 ops/ms
Iteration 3: 12.981 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
13.853 ±(99.9%) 15.571 ops/ms [Average]
(min, avg, max) = (12.981, 13.853, 14.687), stdev = 0.854
CI (99.9%): [≈ 0, 29.425] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 90.00% complete, ETA 00:00:10
# Fork: 1 of 1
# Warmup Iteration 1: 515.059 ops/ms
# Warmup Iteration 2: 563.552 ops/ms
Iteration 1: 535.605 ops/ms
Iteration 2: 561.710 ops/ms
Iteration 3: 561.907 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
553.074 ±(99.9%) 276.013 ops/ms [Average]
(min, avg, max) = (535.605, 553.074, 561.907), stdev = 15.129
CI (99.9%): [277.061, 829.088] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 95.00% complete, ETA 00:00:05
# Fork: 1 of 1
# Warmup Iteration 1: 2.898 ops/ms
# Warmup Iteration 2: 2.928 ops/ms
Iteration 1: 2.959 ops/ms
Iteration 2: 2.940 ops/ms
Iteration 3: 2.979 ops/ms
Result "com.ankurm.listbenchmarks.ListOpsBenchmark.iterateSum":
2.959 ±(99.9%) 0.351 ops/ms [Average]
(min, avg, max) = (2.940, 2.959, 2.979), stdev = 0.019
CI (99.9%): [2.608, 3.310] (assumes normal distribution)
# Run complete. Total time: 00:01:47
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
Benchmark (impl) (size) Mode Cnt Score Error Units
ListOpsBenchmark.addEnd ArrayList 1000 thrpt 3 177916.950 ± 64318.989 ops/ms
ListOpsBenchmark.addEnd ArrayList 100000 thrpt 3 80128.728 ± 122132.789 ops/ms
ListOpsBenchmark.addEnd LinkedList 1000 thrpt 3 46204.377 ± 21973.304 ops/ms
ListOpsBenchmark.addEnd LinkedList 100000 thrpt 3 40902.411 ± 45819.499 ops/ms
ListOpsBenchmark.addFront ArrayList 1000 thrpt 3 6585.099 ± 11228.597 ops/ms
ListOpsBenchmark.addFront ArrayList 100000 thrpt 3 53.499 ± 6.659 ops/ms
ListOpsBenchmark.addFront LinkedList 1000 thrpt 3 42897.540 ± 19001.947 ops/ms
ListOpsBenchmark.addFront LinkedList 100000 thrpt 3 43314.310 ± 43475.313 ops/ms
ListOpsBenchmark.addMiddle ArrayList 1000 thrpt 3 14353.710 ± 15740.742 ops/ms
ListOpsBenchmark.addMiddle ArrayList 100000 thrpt 3 125.695 ± 29.247 ops/ms
ListOpsBenchmark.addMiddle LinkedList 1000 thrpt 3 608.799 ± 98.048 ops/ms
ListOpsBenchmark.addMiddle LinkedList 100000 thrpt 3 5.086 ± 1.014 ops/ms
ListOpsBenchmark.getRandom ArrayList 1000 thrpt 3 75569.691 ± 16253.874 ops/ms
ListOpsBenchmark.getRandom ArrayList 100000 thrpt 3 40457.598 ± 41624.817 ops/ms
ListOpsBenchmark.getRandom LinkedList 1000 thrpt 3 2249.225 ± 7101.544 ops/ms
ListOpsBenchmark.getRandom LinkedList 100000 thrpt 3 19.165 ± 14.662 ops/ms
ListOpsBenchmark.iterateSum ArrayList 1000 thrpt 3 1861.466 ± 1077.953 ops/ms
ListOpsBenchmark.iterateSum ArrayList 100000 thrpt 3 13.853 ± 15.571 ops/ms
ListOpsBenchmark.iterateSum LinkedList 1000 thrpt 3 553.074 ± 276.013 ops/ms
ListOpsBenchmark.iterateSum LinkedList 100000 thrpt 3 2.959 ± 0.351 ops/ms
@@ -0,0 +1,352 @@
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = ArrayDeque, size = 1000)
# Run progress: 0.00% complete, ETA 00:01:00
# Fork: 1 of 1
# Warmup Iteration 1: 225049.942 ops/ms
# Warmup Iteration 2: 216387.082 ops/ms
Iteration 1: 260204.948 ops/ms
Iteration 2: 152481.625 ops/ms
Iteration 3: 199092.571 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
203926.381 ±(99.9%) 985600.610 ops/ms [Average]
(min, avg, max) = (152481.625, 203926.381, 260204.948), stdev = 54024.095
CI (99.9%): [≈ 0, 1189526.991] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = ArrayDeque, size = 100000)
# Run progress: 8.33% complete, ETA 00:00:58
# Fork: 1 of 1
# Warmup Iteration 1: 73559.495 ops/ms
# Warmup Iteration 2: 88126.623 ops/ms
Iteration 1: 82011.079 ops/ms
Iteration 2: 84538.326 ops/ms
Iteration 3: 60837.052 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
75795.485 ±(99.9%) 237457.872 ops/ms [Average]
(min, avg, max) = (60837.052, 75795.485, 84538.326), stdev = 13015.867
CI (99.9%): [≈ 0, 313253.357] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 16.67% complete, ETA 00:00:54
# Fork: 1 of 1
# Warmup Iteration 1: 82032.730 ops/ms
# Warmup Iteration 2: 80584.591 ops/ms
Iteration 1: 79301.950 ops/ms
Iteration 2: 88128.376 ops/ms
Iteration 3: 87233.185 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
84887.837 ±(99.9%) 88631.345 ops/ms [Average]
(min, avg, max) = (79301.950, 84887.837, 88128.376), stdev = 4858.183
CI (99.9%): [≈ 0, 173519.182] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 25.00% complete, ETA 00:00:49
# Fork: 1 of 1
# Warmup Iteration 1: 102529.616 ops/ms
# Warmup Iteration 2: 75609.806 ops/ms
Iteration 1: 86058.866 ops/ms
Iteration 2: 85350.743 ops/ms
Iteration 3: 85011.734 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
85473.781 ±(99.9%) 9747.596 ops/ms [Average]
(min, avg, max) = (85011.734, 85473.781, 86058.866), stdev = 534.299
CI (99.9%): [75726.185, 95221.376] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 33.33% complete, ETA 00:00:43
# Fork: 1 of 1
# Warmup Iteration 1: 212378.579 ops/ms
# Warmup Iteration 2: 218438.486 ops/ms
Iteration 1: 245793.910 ops/ms
Iteration 2: 245870.397 ops/ms
Iteration 3: 242681.893 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
244782.067 ±(99.9%) 33189.080 ops/ms [Average]
(min, avg, max) = (242681.893, 244782.067, 245870.397), stdev = 1819.205
CI (99.9%): [211592.987, 277971.147] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 41.67% complete, ETA 00:00:37
# Fork: 1 of 1
# Warmup Iteration 1: 94135.159 ops/ms
# Warmup Iteration 2: 95652.428 ops/ms
Iteration 1: 98077.379 ops/ms
Iteration 2: 100976.549 ops/ms
Iteration 3: 98379.804 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.offerPollTail":
99144.577 ±(99.9%) 29075.456 ops/ms [Average]
(min, avg, max) = (98077.379, 99144.577, 100976.549), stdev = 1593.724
CI (99.9%): [70069.122, 128220.033] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = ArrayDeque, size = 1000)
# Run progress: 50.00% complete, ETA 00:00:32
# Fork: 1 of 1
# Warmup Iteration 1: 298481.803 ops/ms
# Warmup Iteration 2: 312401.613 ops/ms
Iteration 1: 299087.963 ops/ms
Iteration 2: 298466.157 ops/ms
Iteration 3: 299175.622 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
298909.914 ±(99.9%) 7056.600 ops/ms [Average]
(min, avg, max) = (298466.157, 298909.914, 299175.622), stdev = 386.796
CI (99.9%): [291853.313, 305966.514] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = ArrayDeque, size = 100000)
# Run progress: 58.33% complete, ETA 00:00:26
# Fork: 1 of 1
# Warmup Iteration 1: 106969.537 ops/ms
# Warmup Iteration 2: 90907.103 ops/ms
Iteration 1: 104740.942 ops/ms
Iteration 2: 105037.254 ops/ms
Iteration 3: 100438.523 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
103405.573 ±(99.9%) 46955.844 ops/ms [Average]
(min, avg, max) = (100438.523, 103405.573, 105037.254), stdev = 2573.808
CI (99.9%): [56449.730, 150361.417] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = LinkedList, size = 1000)
# Run progress: 66.67% complete, ETA 00:00:21
# Fork: 1 of 1
# Warmup Iteration 1: 108757.170 ops/ms
# Warmup Iteration 2: 76136.263 ops/ms
Iteration 1: 79754.897 ops/ms
Iteration 2: 87449.217 ops/ms
Iteration 3: 83960.921 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
83721.678 ±(99.9%) 70288.226 ops/ms [Average]
(min, avg, max) = (79754.897, 83721.678, 87449.217), stdev = 3852.735
CI (99.9%): [13433.452, 154009.905] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = LinkedList, size = 100000)
# Run progress: 75.00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration 1: 110204.142 ops/ms
# Warmup Iteration 2: 74155.001 ops/ms
Iteration 1: 86320.175 ops/ms
Iteration 2: 83052.660 ops/ms
Iteration 3: 84954.624 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
84775.820 ±(99.9%) 29939.400 ops/ms [Average]
(min, avg, max) = (83052.660, 84775.820, 86320.175), stdev = 1641.080
CI (99.9%): [54836.420, 114715.220] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = ArrayList, size = 1000)
# Run progress: 83.33% complete, ETA 00:00:10
# Fork: 1 of 1
# Warmup Iteration 1: 11212.051 ops/ms
# Warmup Iteration 2: 11024.015 ops/ms
Iteration 1: 11274.928 ops/ms
Iteration 2: 11150.226 ops/ms
Iteration 3: 10960.108 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
11128.421 ±(99.9%) 2892.341 ops/ms [Average]
(min, avg, max) = (10960.108, 11128.421, 11274.928), stdev = 158.539
CI (99.9%): [8236.080, 14020.762] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 25.0.4.1, OpenJDK 64-Bit Server VM, 25.0.4.1+1-LTS
# VM invoker: /tmp/tools/jdk-25.0.4.1+1/bin/java
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead
# Parameters: (impl = ArrayList, size = 100000)
# Run progress: 91.67% complete, ETA 00:00:05
# Fork: 1 of 1
# Warmup Iteration 1: 42.851 ops/ms
# Warmup Iteration 2: 57.144 ops/ms
Iteration 1: 54.588 ops/ms
Iteration 2: 57.561 ops/ms
Iteration 3: 55.350 ops/ms
Result "com.ankurm.listbenchmarks.DequeBenchmark.pushPopHead":
55.833 ±(99.9%) 28.174 ops/ms [Average]
(min, avg, max) = (54.588, 55.833, 57.561), stdev = 1.544
CI (99.9%): [27.658, 84.007] (assumes normal distribution)
# Run complete. Total time: 00:01:04
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
Benchmark (impl) (size) Mode Cnt Score Error Units
DequeBenchmark.offerPollTail ArrayDeque 1000 thrpt 3 203926.381 ± 985600.610 ops/ms
DequeBenchmark.offerPollTail ArrayDeque 100000 thrpt 3 75795.485 ± 237457.872 ops/ms
DequeBenchmark.offerPollTail LinkedList 1000 thrpt 3 84887.837 ± 88631.345 ops/ms
DequeBenchmark.offerPollTail LinkedList 100000 thrpt 3 85473.781 ± 9747.596 ops/ms
DequeBenchmark.offerPollTail ArrayList 1000 thrpt 3 244782.067 ± 33189.080 ops/ms
DequeBenchmark.offerPollTail ArrayList 100000 thrpt 3 99144.577 ± 29075.456 ops/ms
DequeBenchmark.pushPopHead ArrayDeque 1000 thrpt 3 298909.914 ± 7056.600 ops/ms
DequeBenchmark.pushPopHead ArrayDeque 100000 thrpt 3 103405.573 ± 46955.844 ops/ms
DequeBenchmark.pushPopHead LinkedList 1000 thrpt 3 83721.678 ± 70288.226 ops/ms
DequeBenchmark.pushPopHead LinkedList 100000 thrpt 3 84775.820 ± 29939.400 ops/ms
DequeBenchmark.pushPopHead ArrayList 1000 thrpt 3 11128.421 ± 2892.341 ops/ms
DequeBenchmark.pushPopHead ArrayList 100000 thrpt 3 55.833 ± 28.174 ops/ms
@@ -0,0 +1,10 @@
Removing every 3rd element of a 200000-element list via Iterator.remove()
ArrayList (iterator.remove, every 3rd): 1,314,187,777 ns (1314.19 ms)
LinkedList (iterator.remove, every 3rd): 5,019,906 ns (5.02 ms)
Ratio (ArrayList time / LinkedList time): 261.80x
ArrayList.remove(index) still shifts every element after the removed one,
even though the iterator was already positioned on it. LinkedList's
iterator.remove() unlinks the node the cursor holds - no shift, no search.
This is the one removal pattern where LinkedList is the right call.
@@ -0,0 +1,4 @@
-------------------------------------------------------------------------------
Test set: com.ankurm.listbenchmarks.CorrectnessTest
-------------------------------------------------------------------------------
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.259 s -- in com.ankurm.listbenchmarks.CorrectnessTest
+83
View File
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ankurm</groupId>
<artifactId>java-core-examples</artifactId>
<version>1.0</version>
</parent>
<artifactId>list-benchmarks</artifactId>
<name>list-benchmarks</name>
<description>ArrayList vs LinkedList vs ArrayDeque: JMH throughput for add/get/iterate/remove, and why LinkedList rarely wins.</description>
<properties>
<jmh.version>1.37</jmh.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>benchmarks</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>25</release>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Regenerates every file in output/. Run from the list-benchmarks module directory.
set -euo pipefail
cd "$(dirname "$0")/.."
mvn -q -B package
java -jar target/benchmarks.jar ListOpsBenchmark 2>&1 \
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING|^Benchmark result is saved to' \
> output/01-list-ops-sweep.txt
java -jar target/benchmarks.jar DequeBenchmark 2>&1 \
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING|^Benchmark result is saved to' \
> output/02-deque-ops-sweep.txt
java -cp target/classes com.ankurm.listbenchmarks.IteratorRemovalDemo 2>&1 \
| grep -vE 'JAVA_TOOL_OPTIONS|Djavax\.net\.ssl|^WARNING' \
> output/03-iterator-removal-demo.txt
mvn -q -B test
cp target/surefire-reports/com.ankurm.listbenchmarks.CorrectnessTest.txt output/04-correctness-test.txt
echo "Regenerated output/01-04."
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run one JMH benchmark class ad hoc, e.g.: ./scripts/run.sh ListOpsBenchmark -t 1
set -euo pipefail
cd "$(dirname "$0")/.."
mvn -q -B package -DskipTests
java -jar target/benchmarks.jar "$@"
@@ -0,0 +1,84 @@
package com.ankurm.listbenchmarks;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Stack/queue-shaped workload: push a value in, pop it back out, repeatedly, on a deque that
* already holds {@code size} elements. This is the workload the article's "when ArrayDeque fits
* instead of either" section is about - it is neither "random access" (ArrayList's strength) nor
* "insert given a list reference" (the only thing LinkedList is actually good at), it is the far
* more common "push/pop/offer/poll from one or both ends" shape.
*
* <p>{@code ArrayDeque} is not a {@link List} - it deliberately does not implement
* {@code get(index)} - which is itself part of the article's point: reach for it when you need a
* stack or a queue, not a list, and the API stops you from doing index math you did not mean to
* do.
*
* <p>See {@code output/02-deque-ops-sweep.txt} for the captured run.
*/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
public class DequeBenchmark {
@Param({"ArrayDeque", "LinkedList", "ArrayList"})
public String impl;
@Param({"1000", "100000"})
public int size;
private Deque<Integer> deque; // used for ArrayDeque and LinkedList
private List<Integer> arrayListAsStack; // used only for the ArrayList "anti-pattern" arm
@Setup(Level.Trial)
public void setup() {
switch (impl) {
case "ArrayDeque" -> deque = new ArrayDeque<>(size);
case "LinkedList" -> deque = new LinkedList<>();
case "ArrayList" -> arrayListAsStack = new ArrayList<>(size);
default -> throw new IllegalStateException(impl);
}
if ("ArrayList".equals(impl)) {
for (int i = 0; i < size; i++) arrayListAsStack.add(i);
} else {
for (int i = 0; i < size; i++) deque.addLast(i);
}
}
/** push then pop at the head - the natural operation for a real Deque. */
@Benchmark
public void pushPopHead(Blackhole bh) {
if (arrayListAsStack != null) {
// The only way to get "push/pop at the front" semantics out of a List is
// add(0, x) / remove(0) - an O(n) shift on every single call. That cost IS the point.
arrayListAsStack.add(0, -1);
bh.consume(arrayListAsStack.remove(0));
} else {
deque.addFirst(-1);
bh.consume(deque.pollFirst());
}
}
/** offer then poll at the tail - the natural operation for a real Queue. */
@Benchmark
public void offerPollTail(Blackhole bh) {
if (arrayListAsStack != null) {
arrayListAsStack.add(-1);
bh.consume(arrayListAsStack.remove(arrayListAsStack.size() - 1));
} else {
deque.addLast(-1);
bh.consume(deque.pollLast());
}
}
}
@@ -0,0 +1,73 @@
package com.ankurm.listbenchmarks;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* The one shape of removal where {@code LinkedList} can actually win: removing through an
* {@link Iterator} cursor you are already standing on, instead of by index.
*
* <p>{@code ArrayList.remove(index)} is always an array shift, even when the index came from an
* iterator that was already sitting on the element - the iterator does not save it from the
* shift. {@code LinkedList}'s {@code Iterator.remove()} unlinks the current node directly, no
* traversal needed, because the cursor already holds the node reference.
*
* <p>This is a plain wall-clock illustration with {@code System.nanoTime()}, not a JMH
* benchmark - JIT warmup and GC are not controlled for the way {@link ListOpsBenchmark} controls
* for them. Read it as "yes, this case is real", not as a throughput number to quote. The JMH
* numbers for the general case are in {@code output/01-list-ops-sweep.txt} and
* {@code output/02-deque-ops-sweep.txt}.
*/
public final class IteratorRemovalDemo {
private IteratorRemovalDemo() {}
public static void main(String[] args) {
int size = 200_000;
System.out.println("Removing every 3rd element of a " + size + "-element list via Iterator.remove()");
System.out.println();
long arrayListNanos = removeEveryThird(buildArrayList(size));
long linkedListNanos = removeEveryThird(buildLinkedList(size));
System.out.printf("ArrayList (iterator.remove, every 3rd): %,d ns (%.2f ms)%n",
arrayListNanos, arrayListNanos / 1_000_000.0);
System.out.printf("LinkedList (iterator.remove, every 3rd): %,d ns (%.2f ms)%n",
linkedListNanos, linkedListNanos / 1_000_000.0);
System.out.printf("Ratio (ArrayList time / LinkedList time): %.2fx%n",
(double) arrayListNanos / linkedListNanos);
System.out.println();
System.out.println("ArrayList.remove(index) still shifts every element after the removed one,");
System.out.println("even though the iterator was already positioned on it. LinkedList's");
System.out.println("iterator.remove() unlinks the node the cursor holds - no shift, no search.");
System.out.println("This is the one removal pattern where LinkedList is the right call.");
}
private static long removeEveryThird(List<Integer> list) {
long start = System.nanoTime();
int i = 0;
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
it.next();
if (i % 3 == 0) {
it.remove();
}
i++;
}
return System.nanoTime() - start;
}
private static List<Integer> buildArrayList(int size) {
List<Integer> l = new ArrayList<>(size);
for (int i = 0; i < size; i++) l.add(i);
return l;
}
private static List<Integer> buildLinkedList(int size) {
List<Integer> l = new LinkedList<>();
for (int i = 0; i < size; i++) l.add(i);
return l;
}
}
@@ -0,0 +1,87 @@
package com.ankurm.listbenchmarks;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* Throughput comparison of {@link ArrayList} and {@link LinkedList} on the five operations
* that actually show up in real code: append, random-index read, full iteration, and
* insert/remove at the front and in the middle.
*
* <p>Every mutating benchmark does an insert immediately followed by the matching removal so
* the list's size (and therefore the cost profile) stays constant across the whole measurement
* window instead of drifting as the iteration runs. That is what keeps {@code addFront} and
* {@code addFront} comparable at iteration 1 and iteration 1,000,000 of the same run.
*
* <p>Referenced from the "ArrayList vs LinkedList in 2026" post - see
* {@code output/01-list-ops-sweep.txt} for the captured run this repository ships.
*/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
public class ListOpsBenchmark {
@Param({"ArrayList", "LinkedList"})
public String impl;
@Param({"1000", "100000"})
public int size;
private List<Integer> list;
private final Random rnd = new Random(42);
@Setup(Level.Trial)
public void setup() {
list = "ArrayList".equals(impl) ? new ArrayList<>(size) : new LinkedList<>();
for (int i = 0; i < size; i++) {
list.add(i);
}
}
/** Append one element at the end, then remove it. Net size unchanged. */
@Benchmark
public void addEnd(Blackhole bh) {
list.add(size, -1);
bh.consume(list.remove(size));
}
/** Insert at index 0, then remove index 0. The operation ArrayList is worst at. */
@Benchmark
public void addFront(Blackhole bh) {
list.add(0, -1);
bh.consume(list.remove(0));
}
/** Insert at the midpoint, then remove it again. */
@Benchmark
public void addMiddle(Blackhole bh) {
int mid = size / 2;
list.add(mid, -1);
bh.consume(list.remove(mid));
}
/** Random-access read by index - no mutation, size never changes. */
@Benchmark
public int getRandom() {
return list.get(rnd.nextInt(size));
}
/** Full forward iteration summing every element, via the iterator (the idiomatic way). */
@Benchmark
public long iterateSum() {
long sum = 0;
for (int v : list) {
sum += v;
}
return sum;
}
}
@@ -0,0 +1,69 @@
package com.ankurm.listbenchmarks;
import org.junit.jupiter.api.Test;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Sanity checks only - these do not prove throughput, they prove the two data structures stay
* behaviourally interchangeable through {@link List} so the benchmark's "same workload, two
* implementations" framing is actually true, and pin the one behavioural difference the article
* leans on: {@code ArrayDeque} is not a {@link List}.
*/
class CorrectnessTest {
@Test
void arrayListAndLinkedListAgreeOnContents() {
List<Integer> a = new ArrayList<>();
List<Integer> l = new LinkedList<>();
for (int i = 0; i < 1000; i++) {
a.add(i);
l.add(i);
}
assertEquals(a, l, "List.equals is content-based, not implementation-based");
assertEquals(a.get(500), l.get(500));
}
@Test
void addFrontShiftsArrayListButNotLinkedList() {
List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
List<Integer> l = new LinkedList<>(List.of(1, 2, 3));
a.add(0, 0);
l.add(0, 0);
assertEquals(List.of(0, 1, 2, 3), a);
assertEquals(List.of(0, 1, 2, 3), l);
// Both produce the same *result* - the benchmark exists because the *cost* differs.
}
@Test
void arrayDequeIsNotAList() {
Deque<Integer> d = new ArrayDeque<>();
assertThrows(ClassCastException.class, () -> {
@SuppressWarnings({"unchecked", "rawtypes"})
List<Integer> asList = (List) d; // compiles only via raw type + unchecked cast
asList.get(0);
}, "ArrayDeque deliberately does not implement List - there is no get(index)");
}
@Test
void arrayDequeAndLinkedListAgreeOnStackOrder() {
Deque<Integer> ad = new ArrayDeque<>();
Deque<Integer> ll = new LinkedList<>();
for (int v : new int[] {1, 2, 3}) {
ad.push(v);
ll.push(v);
}
// Both are LIFO via push/pop - same observable order, different internal layout.
assertEquals(3, ad.pop());
assertEquals(3, ll.pop());
assertEquals(2, ad.pop());
assertEquals(2, ll.pop());
}
}
+1
View File
@@ -28,6 +28,7 @@
<module>strings</module>
<module>collections</module>
<module>nio2</module>
<module>list-benchmarks</module>
</modules>
<properties>