I'm FanJae.

[20260512] C# ( List ) 본문

Unity/Unity 초격차캠프

[20260512] C# ( List )

FanJae 2026. 5. 12. 16:53

1. List

(1) 정의

- List<T>는 C#의 제네릭 기반 동적 배열 클래스이다.

- 배열처럼 인덱스로 접근할 수 있으며, 필요할 때 내부 크기가 자동으로 증가한다.

List<int> scores = new List<int>();

scores.Add(90);
scores.Add(85);
scores.Add(100);
Console.WriteLine(scores[0]);

- 하지만 배열과 달리 처음부터 크기를 정확하게 정할 필요는 없다.


(2) 기존 배열(Array)의 문제점

int[] scores = new int[3];

scores[0] = 90;
scores[1] = 85;
scores[2] = 100;
// scores[3] = 70; // IndexOutOfRangeException이 발생

- 기존 배열은 배열의 크기를 초과하면 오류가 발생한다.

- 위 코드는 IndexOutOfRnageException 이 발생한다.

- 배열 크기를 늘리려면 새로운 배열을 만들어야 한다.

 

int [] newSocres = new int[4];

for(int i = 0; i < socres.Length; i++)
{
	newScores[i] = scores[i];
}
newSocres[3] = 70;
scores = newScores;

 

① 배열의 문제점

- 배열의 크기를 중간에 변경하기 어렵다.

- 크기를 늘릴 때 마다 새로운 배열의 생성 및 기존 데이터 복사가 필요하다.

- 데이터가 많아질수록 복사 비용도 증가한다.


(3) ArrayList

- List<T> 가 나오기 전에 사용했다.

- 배열처럼 동작하지만, 크기가 자동으로 증가하눈 구조였다.

ArrayList list = new ArrayList();

list.Add(90);
list.Add(100);
list.Add(200);

- 형태만 보면 배열과 크게 다르지 않았다.

 

① 장점

- 어떤 타입이든 제약을 받지 않고 넣을 수 있다.

크기를 자동으로 늘려준다.

 

② ArrayList의 문제점 

- 아무 타입이나 다 들어간다.

- 타입 안정성이 없다.

ArrayList list = new ArrayList();

list.Add(90);
list.Add("FanJae");
list.Add(3.14f);

// int, float, string 모든 타입이 다 들어가고 있다.

- int, float, string 모든 타입이 다 들어가고 있다.

foreach(object obj in list)
{
    int score = (int)obj;
}

- 위와 같은 작업을 진행하면 런타임에서 예외가 발생한다.

- 위와 같이 값 형식을 참조 형식으로 변환 또는 반대 작업은 권장되는 작업이 아니다. (이 문제에 대해서는 박싱 & 언박싱 문제에서 다룬다.)


(4) List<T>

- 위와 같은 문제를 해결하기 위해 많이 사용하는 것이 List<T> 이다.

List<int> list = new List<int>();

list.Add(90);
list.Add(100);
list.Add(200);

- List<int>는 int만 저장 가능하다.

list.Add("FanJae");

- 위 코드는 컴파일 오류가 발생한다.

- 즉, 잘못된 타입이 들어오는 것을 미리 막아준다.


(5) List의 특징

① 제네릭 기반

- List<T> 는 제네릭 클래스이다.

- 타입 안정성이 유지된다.

- 형 변환이 필요없다.

 

② 동적 크기 조정

- 내부적으로는 배열을 사용한다.

- 하지만 공간이 부족하면 자동으로 더 큰 배열을 만든다.

items // 실제 데이터 배열
size // 실제 저장된 데이터 개수
capacity // 현재 배열 크기

- List가 내부적으로 가지는 데이터는 이와 같다.

 

③ 용량 증가 방식

List<int> list = new List<int>();

- 데이터를 추가하다가 공간이 부족하면 아래와 같이 재할당(Reallocation)이 일어난다.

 

1. 더 큰 배열이 생성된다.
2. 기존 데이터를 복사한다.
3. 기존 배열 대신 새 배열을 사용한다.

 

④ 배열처럼 인덱스 접근 가능

Console.WriteLine(list[0]);

- 배열처럼 빠르게 접근이 가능하다.


(6) 주요 메서드

① Add

list.Add(10);

리스트 끝에 데이터를 추가한다.

 

② Insert

list.Insert(1, 50);

특정 위치에 데이터를 삽입한다.

 

③ Remove

List.Remove(10);

특정 데이터를 제거한다.

 

④ Clear

list.Clear();

모든 데이터를 제거한다.

- 단, 할당되어 있는 공간(Capacity)를 회수해가지 않는다.

 

⑤ Contains

list.Contains(100);

특정 데이터 존재 여부를 확인한다.

 

⑥ IndexOf

list.IndexOf(100);

특정 데이터 위치 반환한다. 없으면 -1을 반환한다.

 

⑦ Sort

list.Sort();

- list에 대한 정렬을 진행한다. (기본적으로 오름차순이다.)


(7) List의 장점

- 배열보다 사용이 편리하다.

- 크기를 자동으로 조정해준다.

- 타입 안정성이 유지된다.

- 박싱 / 언박싱 문제를 줄일 수 있다.

- 배열처럼 인덱스 접근이 가능하다.


(8) 주의점

- 내부적으로 배열을 사용하므로, 중간 삽입 / 삭제를 진행하면 데이터 이동 비용이 발생한다.

- 용량이 부족해 재할당이 발생하면 기존 데이터 복사가 필요하다.

- 따라서 보통 Capacity를 미리 지정하여 사용하기도 한다.

List<string> items = new List<string>(200);

- 예상 개수를 미리 생각해서 내부 배열 크기를 잡아 재할당을 줄일 수 있다.

- List 자체와 내부 배열은 참조 타입이다. 따라서 타입 객체이므로 이는 GC 대상이다. 다만, ArrayList 처럼 값 형식을 object 로 저장할 때 발생하는 박싱 / 언박싱을 줄일 수 있다.

- 따라서, 불필요한 힙 할당과 GC 부담을 줄이는데 도움이 된다.


(9) 정리

- List<T>는 제네릭 기반 동적 배열 클래스이다.

- 배열처럼 인덱스로 접근 가능하면서도 크기를 자동으로 조절할 수 있다.

- 기존 배열의 고정 크기 문제와 ArrayList의 타입 안정성 문제를 개선했다.

- 내부적으로는 배열을 사용하며, 공간이 부족하면 더 큰 배열을 복사하고, 데이터를 복사한다.

 

 
Comments