Quick Reference
크기가 정해진 순차 데이터는 T[], 추가·삭제로 길이가 달라지는 순차 데이터는 List<T>를 먼저 고릅니다. 둘 다 인덱스로 읽지만 배열은 길이가 고정되고, List<T>는 Count와 저장 공간인 Capacity를 구분합니다.
// 배열 — 고정 길이
int[] scores = { 85, 90, 78 };
string?[] slots = new string?[3]; // 초기값 null
int first = scores[0]; // 인덱스 접근
// List<T> — 가변 길이
var names = new List<string> { "Mina", "Jin" };
names.Add("Sol");
names.Remove("Jin");
int count = names.Count;
int capacity = names.EnsureCapacity(16); // 앞으로 16개까지 담을 공간 확보문법
어떤 컬렉션을 먼저 떠올리면 되나
가장 먼저 구분할 것은 배열 T[] 과 List<T> 입니다.
int[] fixedScores = { 85, 90, 78 }; // 길이 고정
var dynamicScores = new List<int> { 85, 90 }; // 길이 변경 가능- 크기가 처음부터 정해져 있고 바뀌지 않으면 배열
- 추가/삭제가 자연스럽게 들어오면
List<T> - 둘 다 인덱스로 읽을 수 있지만, 길이와 확장 방식이 다릅니다
배열 — 고정 길이 인덱스 저장소
배열은 고정 개수의 같은 타입 요소를 인덱스로 다루는 참조 타입입니다. new int[5]를 만든 뒤 길이는 바꿀 수 없고, 인덱스 접근(arr[i])은 O(1)입니다. 구현 세부 메모리 배치보다 길이가 고정되어 있고 0부터 Length - 1까지 접근한다는 계약을 기준으로 사용합니다.
int[] arr = new int[5]; // 모두 0으로 초기화
int[] arr2 = { 1, 2, 3 }; // 선언과 동시에 초기화
int[] arr3 = new int[] { 1, 2, 3 }; // 동일한 의미
// 길이와 범위
Console.WriteLine(arr2.Length); // 3
Console.WriteLine(arr2[0]); // 1 (0-based index)
Console.WriteLine(arr2[^1]); // 3 (마지막 요소, C# 8+)
int[] tail = arr2[1..]; // {2, 3}: 새 배열을 만들어 복사 (C# 8+)배열을 초기화하면 숫자 타입은 0, bool은 false, 참조 타입은 null로 채워집니다.
List<T> — 내부적으로 배열을 동적으로 확장
List<T>는 내부 배열을 바탕으로 길이를 늘립니다. Add() 시 용량이 부족하면 더 큰 저장소를 확보하고 기존 요소를 복사할 수 있습니다. 성장 비율은 구현 세부이므로 2배라고 가정하지 않습니다. 추가할 수를 대략 안다면 생성자 capacity나 EnsureCapacity로 재할당 횟수를 줄입니다.
var list = new List<int>(100); // capacity 100으로 시작 — 재할당 방지
list.Add(1);
list.AddRange(new[] { 2, 3, 4 }); // 여러 항목 한 번에 추가
Console.WriteLine(list.Count); // 실제 요소 수: 4
Console.WriteLine(list.Capacity); // 확보한 저장 공간: 100 이상
list.EnsureCapacity(200); // 최소 200개를 담을 공간 확보
list.Insert(0, 0); // 특정 위치에 삽입 — O(n)
list.Remove(3); // 값으로 제거 — O(n)
list.RemoveAt(0); // 인덱스로 제거 — O(n)
bool has = list.Contains(2); // O(n)
int idx = list.IndexOf(2); // -1이면 없음
list.Sort(); // 정렬
list.Reverse(); // 역순다차원 배열과 가변 배열
다차원 배열은 행렬처럼 2개 이상의 차원을 가집니다. 가변 배열(jagged array) 은 각 행의 길이가 다를 수 있는 배열의 배열입니다.
// 2차원 배열 (직사각형)
int[,] matrix = new int[3, 4]; // 3행 4열
matrix[0, 0] = 1;
// 가변 배열 (각 행 길이가 다름)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5 };
jagged[2] = new int[] { 6 };배열과 List<T> 변환
int[] arr = { 1, 2, 3 };
List<int> list = arr.ToList(); // 배열 → List
int[] arr2 = list.ToArray(); // List → 배열
// IEnumerable<T>에서 변환
int[] filtered = list.Where(x => x > 1).ToArray();배열과 List 선택
| 구조 | 특징 | 크기 | 인덱스 접근 |
|---|---|---|---|
배열 T[] | 고정 길이, 순차 인덱스 | Length | O(1) |
List<T> | 가변 길이, 내부 배열 기반 | Count | O(1) |
| 선택 기준 | 크기 변경 없음 → 배열, 추가/삭제 → List<T> | ||
Add, Remove | O(1) 평균, O(n) 최악 (재할당/이동) |
int[] arr = { 1, 2, 3 };
var list = new List<int> { 1, 2, 3 };
// ❌ 배열은 Add가 없다
// arr.Add(4);
// ✅ List<T>는 길이를 늘릴 수 있다
list.Add(4);주의할 점
List<T>의 Insert()와 Remove()는 O(n) 입니다. 중간 삽입·삭제가 정말 병목인지 먼저 측정하세요. 이미 노드 위치를 알고 있고 순차 탐색 비용을 감당할 수 있을 때만 LinkedList<T>가 후보가 됩니다. 인덱스 접근이나 값 검색부터 해야 한다면 대개 List<T>가 더 단순하고 빠릅니다.
배열의 범위 초과 접근은 IndexOutOfRangeException을 발생시킵니다. arr[arr.Length - 1] 대신 C# 8+의 인덱스 연산자 arr[^1]을 쓰면 실수를 줄일 수 있습니다.
참고 링크
2 sources