리스트(List)라는 추상 자료형(ADT, Abstract Data Tye)으로 '순서가 있는 데이터의 집합'을 의미한다.
리스트는 개념일 뿐, 실제 구현 방법은 아니라는 것이다. "순서가 있는 데이터를 저장하라"는 명령을 받았을 때, 컴퓨터의 메모리 구조상 이를 구현하는 방법은 크게 두 가지로 나뉜다.
이 두 방식의 차이는 메모리를 어떻게 사용할 것인가에 대한 근본적인 철학의 차이이며, 이 차이가 모든 성능 의슈의 시작점이 된다. 이번 글에서는 이 두 자료구조의 내부를 깊이 있게 파헤쳐보자!
배열은 가장 원시적이면서도 강력한 자료구조다. 배열의 가장 큰 특징은 물리적 주소의 연속성(Contiguous Memory)이다. 선언하는 순간, 메모리 상의 연속된 공간을 독점한다.
데이터가 연속되어 있다는 것은 '수학적 계산'이 가능하다는 뜻이다. 배열의 시작 주소만 알면, 100번째 데이터의 위치는 즉시 알 수 있다.
이 공식 덕분에 배열은 데이터가 10개든 100만개든 상관없이, 조회 속도가 로 일정하다.
int arr[10]으로 선언하면, 11번째 데이터는 넣을 수 없다. 공간을 늘리려면 새로운 배열을 만들고 이사(Migration)를 가야 한다.우리가 실무에서 쓰는 Java의 ArrayList, C++의 std::vector, Python의 list는 사실 고정된 배열의 단점을 보완한 동적 배열이다.
배열이 '연속성'에 집착했다면, 연결 리스트는 '불연속성'을 택했다. 데이터는 메모리 여기저기에 흩어져 있고, 각 데이터(Node)가 다음 데이터의 주소(Reference)를 들고 있는 형태다.
가장 기본적인 형태. 노드는 [Data | Nest]로 구성된다.
단방향의 단점을 극복하기 위해 [Pref | Data | Next] 구조를 가진다.
Prev와 Next를 서로 연결해주면 끝이다. 즉, 삭제 연산 자체는 이다. (단, 삭제할 노드를 찾는 탐색 시간은 별개)LinkedList는 바로 이 이중 연결 리스트로 구현되어 있다. 탐색과 조작의 유연성을 위해 메모리(Prev 포인터)를 조금 더 쓰는 트레이드 오프를 선택한 것이다.마지막 노드(Tail)가 null을 가리키는 대신, 다시 첫 번재 노드(Head)를 가리키는 구조다.
자료구조를 선택할 때는 단순히 "배열이냐 리스트냐"를 넘어, 언어 차원에서 제공하는 동적 배열(ArrayList)과 기본 배열(Array)의 차이까지 명확하게 구분해야 한다. 이 셋의 성능과 특징을 비교해보자.

⚠️ 용어 구분: 접근 VS 검색
- 접근(Access): 3번째 데이터 내놔 (인덱스 기반) ->
- 검색(Search): 값이 '사과'인 데이터 어딨어? (값 기반) ->
ArrayList가 빠르다는 건 접근이 빠르다는 뜻이다. 값을 찾는 건 셋 다 똑같이 처음부터 뒤져야 하므로 느리다.
int[] arr = new int[10]; 처럼 선언 시 크기가 고정된다.get(999)를 호출하면 0번부터 세는 게 아니라, 수학 공식()으로 단 한번에 위치를 찾아낸다. 데이터가 100만개여도 조회 속도는 이다.ArrayList와의 결정적 차이이다.Prev, Next)까지 저장해야 하므로 메모리를 3~4배 더 먹는다.💡 핵심 팁: ArrayList의 비밀
ArrayList의add연산은 기본적으로 이지만, 배열이 꽉 차서 확장(Resizing)이 되는 순간에는 이 걸린다. 이를 Amortized (분할 상환시간)이라고 부른다. 즉 가끔 느려지긴 하지만 평균적으로는 빠르다는 뜻이다. 만약 데이터 개수를 대략 안다면new ARrayList<>(1000)처럼 초기 크기를 지정해주는 것이 Resizing을 막아 성능을 높이는 꿀팁입니다.
이론상 리스트의 삽입이 더 빠른데, 왜 실제로는 ArrayList를 더 많이 사용할까? 정답은 하드웨어(CPU 캐시)에 있다.
CPU는 메모리에서 데이터를 가져올 때, 딱 1개(4byte)만 가져오지 않는다. 인접한 데이터가 곧 사용될 것이라 예측하고 주변 데이터 블록(Cache Line, 보통 64byte)을 통째로 가져온다. 이를 공간 지역성(Spatical Locality)이라 한다.
arr[0]을 읽을 때, arr[1], arr[2]...도 같이 캐시에 로드된다. 즉, 캐시 적중률(Cache Hit Rate)이 매우 높다.데이터의 개수가 수만 개 수준으로 많지 않거나, 삽입/삭제가 극단적으로 빈번하지 않다면, 최신 하드웨어에서는 배열(ArrayList)이 연결 리스트보다 전반적으로 빠르다. 캐시 효율성 때문이다.
데이터 개수가 고정되어 있나요?
├─ YES → Primitive 타입인가요?
│ ├─ YES → Array (int[], double[] 등)
│ └─ NO → 편의성이 중요한가요?
│ ├─ YES → ArrayList
│ └─ NO → Array
│
└─ NO → 어떤 연산이 더 빈번한가요?
├─ 조회 (Read) > 수정 → ArrayList
├─ 수정 (Insert/Delete) > 조회 → LinkedList
└─ 조회 ≈ 수정 → ArrayList (캐시 효율 때문)
| 상황 / 키워드 | 추천 자료구조 | 이유 |
|---|---|---|
| "N번째 값을 출력하라", "이분 탐색", "정렬" | Array / ArrayList | 인덱스 접근()이 필수적임. |
| "커서를 이동하며 글자 삭제", "원형으로 앉아서..." | LinkedList | 중간 삭제/삽입이 핵심 로직임. |
| "데이터가 계속 들어오는데 최대 N개까지만 유지" | LinkedList (Deque) | 앞뒤로 넣고 빼는 작업에 최적화됨. |