@@ -46,6 +46,9 @@ Big-O는 연산 횟수의 증가율을 설명합니다. 하지만 실제 실행
4646` ArrayList ` 는 내부적으로 ` Object[] ` 배열을 사용합니다.[ ^ arraylist-javadoc ]
4747반면 ` LinkedList ` 는 노드(prev/item/next)가 연결된 구조입니다.[ ^ linkedlist-javadoc ]
4848
49+ [ ^ arraylist-javadoc ] : ArrayList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayList.html
50+ [ ^ linkedlist-javadoc ] : LinkedList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html
51+
4952```
5053ArrayList (contiguous references)
5154[ref][ref][ref][ref][ref]...
@@ -78,6 +81,8 @@ CPU는 메모리를 바이트 단위가 아니라 **cache line 단위**로 가
7881
7982Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.[ ^ devjava-al-vs-ll ]
8083
84+ [ ^ devjava-al-vs-ll ] : ArrayList vs LinkedList, dev.java (Oracle) — https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/
85+
8186---
8287
8388## 4) "LinkedList 삽입 O(1)"의 실무 함정
@@ -96,6 +101,8 @@ Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.
96101
97102` RandomAccess ` 마커 인터페이스가 존재하는 이유도 바로 이 차이를 알고리즘이 구분하기 위함입니다.[ ^ randomaccess-javadoc ]
98103
104+ [ ^ randomaccess-javadoc ] : RandomAccess Javadoc, Oracle Java SE 17 — https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/RandomAccess.html
105+
99106---
100107
101108## 5) Java 2D 배열: "완전 flat"이 아니라 array-of-arrays
@@ -104,6 +111,8 @@ Oracle dev.java의 공식 비교 자료도 이 점을 명확히 설명합니다.
104111
105112Java의 ` int[][] ` 는 C 스타일의 단일 연속 2D 블록이 아니라, ** 배열의 배열** 입니다.[ ^ jls-arrays ]
106113
114+ [ ^ jls-arrays ] : JLS Chapter 10 (Arrays), multidimensional arrays are arrays of arrays — https://docs.oracle.com/javase/specs/jls/se24/html/jls-10.html
115+
107116그래도 아래 순회는 성능 차이가 납니다.
108117
109118``` java
@@ -139,6 +148,8 @@ Javadoc도 직접 이렇게 말합니다.
139148
140149> "ArrayDeque는 queue로 사용할 때 LinkedList보다 faster일 가능성이 높다."[ ^ arraydeque-javadoc ]
141150
151+ [ ^ arraydeque-javadoc ] : ArrayDeque Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayDeque.html
152+
142153물론 예외는 있습니다.
143154
144155- ` null ` 원소를 꼭 저장해야 한다 (` ArrayDeque ` 는 null 금지)
@@ -152,6 +163,9 @@ Javadoc도 직접 이렇게 말합니다.
152163
153164Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공격적인 메모리 최적화를 할 여지를 넓히는 방향입니다.[ ^ jep401 ] [ ^ valhalla ]
154165
166+ [ ^ jep401 ] : JEP 401: Value Classes and Objects (Preview) — https://openjdk.org/jeps/401
167+ [ ^ valhalla ] : OpenJDK Project Valhalla — https://openjdk.org/projects/valhalla/
168+
155169핵심은 "가능성"입니다.
156170
157171- value object는 identity가 없으므로
@@ -177,45 +191,53 @@ Project Valhalla(JEP 401)는 value class/value object를 통해 JVM이 더 공
1771913 . LinkedList를 선택할 때는 "왜 LinkedList여야 하는지"를 코드 코멘트/리뷰에 남김
1781924 . 성능 이슈는 ` System.nanoTime() ` 루프가 아니라 JMH로 검증[ ^ jmh ]
179193
194+ [ ^ jmh ] : OpenJDK JMH — https://github.com/openjdk/jmh
195+
180196Big-O는 여전히 중요합니다. 다만 ** 현대 CPU에서 데이터가 어떻게 배치되고 이동하는지** 까지 같이 봐야, 실제 서비스 성능을 예측할 수 있습니다.
181197
182198---
183199
184- ## 9) 재현 가능한 JMH 예시 (로컬 + GitHub Actions )
200+ ## 9) 재현 가능한 JMH 예시 (로컬 clone 기준 )
185201
186202말로만 "ArrayList가 보통 빠르다"고 끝내지 않기 위해, 이 저장소에 JMH 예시를 같이 두었습니다.
187203
188- - 벤치마크 코드: ` benchmarks/jmh-arraylist-vs-linkedlist/src/main/java/io/clickin/bench/ListAndDequeBenchmark.java `
189- - 실행 워크플로: ` .github/workflows/jmh-collections-benchmark.yml `
204+ - 예제 경로(canonical): ` examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality `
205+ - GitHub 링크 템플릿: ` https://github.com/<owner>/<repo>/tree/main/examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality `
206+ - 벤치마크 코드: ` examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/src/main/java/io/clickin/bench/ListAndDequeBenchmark.java `
207+ - 실행 스크립트: ` examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/run-local-jmh.sh `
190208
191209로컬에서는 아래처럼 동일한 설정으로 바로 실행할 수 있습니다.
192210
193211``` bash
194- mvn -B -ntp -f benchmarks/jmh-arraylist-vs-linkedlist/pom.xml clean package
195- java -jar benchmarks/jmh-arraylist-vs-linkedlist/target/jmh-benchmarks.jar \
196- io.clickin.bench.ListAndDequeBenchmark \
197- -wi 5 -i 8 -w 300ms -r 300ms -f 1 -tu ns \
198- -jvmArgs " -Xms1g -Xmx1g" \
199- -rf json -rff benchmarks/jmh-arraylist-vs-linkedlist/results/jmh-result.json
212+ git clone https://github.com/< owner> /< repo> .git
213+ cd < repo>
214+ ./examples/blog/2026-02-25-arraylist-vs-linkedlist-cache-locality/run-local-jmh.sh
200215```
201216
202- GitHub Actions 실행 시에는 결과(JSON/TXT)와 실행 환경 정보(Java version, CPU 정보)를 artifact로 올려서,
203- "어떤 머신/JDK에서 나온 숫자인지"를 추적 가능하게 했습니다.
217+ 아래는 이 글 작성 시점에 실행한 ** quick run 결과** 입니다.
218+
219+ - 설정: ` ./run-local-jmh.sh --quick ` (quick preset)
220+
221+ | Scenario | size | Array/ArrayDeque (ns/op) | LinkedList (ns/op) | 관측 |
222+ | ---| ---:| ---:| ---:| ---|
223+ | ` getMiddle ` | 65536 | ` ArrayList ` 1.627 | 114522.376 | LinkedList가 약 70,000배 느림 |
224+ | ` iterate ` | 65536 | ` ArrayList ` 167263.719 | 277359.265 | LinkedList가 약 1.66배 느림 |
225+ | ` middleInsertThenRemove ` | 65536 | ` ArrayList ` 3576.092 | 218811.664 | LinkedList가 약 61배 느림 |
226+ | ` headInsertThenRemove ` | 65536 | ` ArrayList ` 7530.176 | 33.369 | LinkedList가 약 225배 빠름 |
227+ | ` tailAddThenRemove ` | 65536 | ` ArrayList ` 15.028 | 31.444 | ArrayList가 약 2.09배 빠름 |
228+ | ` iteratorRemoveAndRestore ` | 65536 | ` ArrayList ` 7434.923 | 25.838 | LinkedList가 약 288배 빠름 |
229+ | ` deque offerLast/pollFirst ` | 65536 | ` ArrayDeque ` 15.159 | 35.446 | ArrayDeque가 약 2.34배 빠름 |
230+ | ` deque offerFirst/pollLast ` | 65536 | ` ArrayDeque ` 14.968 | 34.369 | ArrayDeque가 약 2.30배 빠름 |
231+
232+ 관찰 포인트는 두 가지입니다.
233+
234+ 1 . ` read/iterate/deque ` 계열에서는 Array 구조가 일관되게 유리합니다.
235+ 2 . 반대로 ` head insert/remove ` , ` iterator remove ` 처럼 LinkedList의 구조적 강점이 드러나는 시나리오도 분명히 존재합니다.
236+
237+ 즉 "항상 ArrayList"가 아니라, ** 실제 접근 패턴을 기준으로 선택해야 한다** 가 이 실험의 핵심 결론입니다.
204238
205239결론은 단순합니다.
206240
207241- 이론 복잡도만 보면 LinkedList가 좋아 보일 때가 있어도
208242- 일반적인 Java 애플리케이션의 대부분 시나리오에서는
209243- ` ArrayList ` (그리고 Deque라면 ` ArrayDeque ` )가 더 좋은 기본값입니다.
210-
211- ---
212-
213- [ ^ arraylist-javadoc ] : ArrayList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayList.html
214- [ ^ linkedlist-javadoc ] : LinkedList Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedList.html
215- [ ^ randomaccess-javadoc ] : RandomAccess Javadoc, Oracle Java SE 17 — https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/RandomAccess.html
216- [ ^ devjava-al-vs-ll ] : ArrayList vs LinkedList, dev.java (Oracle) — https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/
217- [ ^ arraydeque-javadoc ] : ArrayDeque Javadoc, Oracle Java SE 25 — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/ArrayDeque.html
218- [ ^ jls-arrays ] : JLS Chapter 10 (Arrays), multidimensional arrays are arrays of arrays — https://docs.oracle.com/javase/specs/jls/se24/html/jls-10.html
219- [ ^ jep401 ] : JEP 401: Value Classes and Objects (Preview) — https://openjdk.org/jeps/401
220- [ ^ valhalla ] : OpenJDK Project Valhalla — https://openjdk.org/projects/valhalla/
221- [ ^ jmh ] : OpenJDK JMH — https://github.com/openjdk/jmh
0 commit comments