CodeGym /행동 /C# SELF /리스트: List<T>

리스트: List<T>

C# SELF
레벨 27, 레슨 2
사용 가능

1. 소개

쇼핑 리스트를 만든다고 상상해봐. 오늘은 3개만 사면 되고, 내일은 10개, 모레는 1개만 필요해. 만약 배열을 쓴다면, 매번 필요한 개수만큼 새 배열을 만들어야 해. 아니면 "혹시 몰라서" 엄청 큰 배열을 만들고 빈 칸을 잔뜩 들고 다녀야겠지. 별로 효율적이지 않지?

근데 만약에 마법 가방이 있어서, 물건을 넣으면 자동으로 커지고, 빼면 작아진다고 생각해봐. 몇 개 넣을지 미리 몰라도 돼. 그냥 넣으면 가방이 알아서 맞춰줘. 이게 바로 C# 프로그래밍 세계에서 List<T>야!

List<T>는 특정 타입의 요소들을 순서대로 저장할 수 있는 제네릭 컬렉션이야. 여기서 핵심은 "동적"이라는 거. 배열이랑 다르게 List<T>는 실행 중에 크기를 바꿀 수 있어. 내부적으로 용량을 알아서 관리하고, 필요하면 늘려줘.

List<T>에서 T는 뭐냐고? 이건 타입 파라미터야. List를 만들 때 T 대신 어떤 타입을 저장할지 정하는 거지. 예를 들어, List<int>는 정수만, List<string>은 문자열만, List<double>은 실수만 저장할 수 있어. 그래서 오렌지 리스트에 실수로 사과를 넣는 일은 없지. 이게 코드의 안정성과 예측 가능성을 보장해줘.

이게 왜 필요할까?

실제 프로젝트에서는 요소 개수를 미리 모르는 경우가 진짜 많아. 예를 들면:

  • 사이트에 접속한 사용자 리스트
  • 온라인 쇼핑몰 장바구니 상품 리스트
  • 검색 결과 리스트
  • 할 일 관리 앱의 할 일 리스트 (이걸 지금 만들 거야!)

이런 경우엔 요소 개수가 계속 바뀌니까 List<T>가 완전 필수야.

List<T>의 주요 메서드

메서드 설명 예시
Add(T item)
리스트 끝에 요소 추가
numbers.Add(5);
Insert(int idx, T)
지정한 위치에 삽입
numbers.Insert(0, 42);
Remove(T item)
첫 번째로 나오는 요소 삭제
numbers.Remove(100);
RemoveAt(int idx)
인덱스로 요소 삭제
numbers.RemoveAt(2);
Clear()
리스트 비우기
numbers.Clear();
Contains(T item)
요소가 있는지 확인
numbers.Contains(10);
IndexOf(T item)
첫 번째로 나오는 인덱스
int pos = numbers.IndexOf(42);
Count
리스트에 있는 요소 개수
int n = numbers.Count;
Capacity
내부 버퍼 용량
numbers.Capacity = 100;

2. 리스트(List<T>) 만들기

실전으로 가보자! 간단한 할 일 관리 앱을 만든다고 해보자. 할 일을 추가할 리스트가 필요하지.

새 List<T>를 만들 때는 new 연산자를 써. 다른 객체 만들 때랑 똑같아.

List 선언하고 만들기

리스트 만드는 문법은 배열이랑 비슷한데, 대괄호 대신 꺾쇠(<>)를 써:

using System.Collections.Generic;

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

상세 설명 좋아하는 사람을 위한 코멘트:
- List<int>는 정수 리스트야(int). int 대신 string, double, 직접 만든 클래스 등 아무 타입이나 넣을 수 있어.
- 만들자마자 리스트는 비어있어 — 길이는 0이야.

예시: 우리 앱의 첫 단계

예를 들어, 예전 강의에서 사용자 이름이랑 나이만 입력받던 앱이 있다고 해보자. 이제 여러 이름을 리스트에 저장해서 나중에 쓸 수 있게 해보자.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> names = new List<string>();

        Console.WriteLine("이름 세 개를 입력하세요:");
        for (int i = 0; i < 3; i++)
        {
            string name = Console.ReadLine();
            names.Add(name);
        }

        Console.WriteLine("입력한 이름:");
        foreach (string n in names)
        {
            Console.WriteLine(n);
        }
    }
}

여기서 Add()에 주목! 이 메서드가 리스트에 새 요소를 추가해. 이게 바로 리스트를 동적으로 만들어주는 거지!

처음부터 요소가 있는 리스트도 만들 수 있어!

List<string> planets = new List<string> { "수성", "금성", "지구" };

처음부터 "스타팅 멤버"를 알고 있을 때 완전 편해. 테스트나 고정 데이터 초기화할 때 자주 써.

3. List<T>의 기본 조작

트릭이나 마법 쓰기 전에, 리스트의 "기본기"부터 익혀보자.

요소 추가: Add()

Add() 메서드는 새 물건을 선반에 올리는 거랑 똑같아:

List<int> numbers = new List<int>();
numbers.Add(42);
numbers.Add(100);

이제 내부에는: [42, 100]

요소 개수 얻기: Count

배열이랑 다르게 리스트는 Length가 아니라 Count라는 속성을 써:

int count = numbers.Count; // 2 반환

기억해: 리스트는 Count, 배열/문자열은 Length야. 비슷해 보여도 다른 속성이야.

인덱스로 요소 접근

방법은 두 가지, 사실상 똑같아:

int first = numbers[0]; // 42
numbers[1] = 200; // 100을 200으로 바꿈

중요: 인덱스는 배열처럼 0부터 시작해. 요소가 3개면 numbers[3]에 접근하면 안 돼!

리스트 요소 순회

for나 foreach 둘 다 쓸 수 있어:

// 인덱스가 필요하면 for
for (int i = 0; i < numbers.Count; i++)
{
    Console.WriteLine(numbers[i]);
}

// 인덱스 필요 없으면 foreach
foreach (int number in numbers)
{
    Console.WriteLine(number);
}

foreach가 보통 더 쉽고 직관적이야, 특히 큰 컬렉션에서.
근데 foreach에서는 인덱스로 요소를 바꾸거나 break로 중간에 나가려면 추가적인 코드가 필요해.

인덱스로 요소 삽입: Insert()

Insert()는 끝이 아니라 아무 위치에나 요소를 넣고 싶을 때 써. 예를 들어 두 번째 자리에:

numbers.Insert(1, 55); // 인덱스 1에 55 삽입

이제 numbers: [42, 55, 200]

요소 삭제

삭제도 추가만큼 쉬워:

numbers.Remove(55); // 55의 첫 번째 등장 삭제
numbers.RemoveAt(0); // 인덱스 0 요소 삭제
numbers.Clear(); // 리스트 전체 비우기

삭제할 때 주의:
- Remove로 없는 값을 지우려 해도 아무 일도 안 일어나. 리스트는 그대로고, 에러도 없어.
- Clear() 후엔 리스트가 다시 비어있어.

4. 유용한 팁

List<T>는 어떻게 커질까?

List<T>에 요소를 추가할 때마다 크기가 조금씩 늘어나는 게 아니야. 대신 List<T>는 미리 여유 있는 메모리 블록(내부 용량, Capacity)을 잡아둬.

요소가 현재 용량을 넘어서면, 더 큰 메모리 블록(보통 2배 크기)을 새로 만들고, 기존 데이터를 복사해:


요소 추가 → 들어감 → 들어감 → 들어감 →
새 요소가 안 들어감 → 더 큰 배열 생성 → 데이터 복사 → 계속 추가

이렇게 하면 Add할 때마다 메모리를 재할당하지 않아도 돼서 더 빨라.

var list = new List<int>();    // Capacity = 0
list.Add(1);                   // Capacity = 4
list.Add(2);                   // Capacity = 4
list.Add(3);                   // Capacity = 4
list.Add(4);                   // Capacity = 4
list.Add(5);                   // Capacity = 8 (확장!)

Capacity vs. Count: 뭐가 다를까?

  • Count — 리스트에 실제로 들어있는 요소 개수
  • Capacity — 요소를 저장하는 내부 배열의 크기
List<int> nums = new List<int>();
Console.WriteLine(nums.Count); // 0
Console.WriteLine(nums.Capacity); // 보통 0 또는 작은 값

nums.Add(123);
Console.WriteLine(nums.Count); // 1
Console.WriteLine(nums.Capacity); // 이제 Count보다 클 수도 있음

nums.Capacity = 1000; // 요소가 많을 걸 알면 Capacity를 미리 늘릴 수도 있어

대부분의 개발자는 Count만 쓰지만, 성능이 중요한 앱(예: 게임, 데이터 분석 등)에서는 Capacity를 조절하는 게 유용할 수 있어.

5. 예시

우리 앱을 좀 더 발전시켜보자 — 이번엔 사용자가 좋아하는 숫자를 추가하고, 리스트에서 삽입/삭제/출력/초기화까지 해보자.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> favoriteNumbers = new List<int>();

        Console.WriteLine("좋아하는 숫자를 입력하세요 (한 줄에 하나씩, 0 입력하면 종료):");
        while (true)
        {
            int num = int.Parse(Console.ReadLine());
            if (num == 0) break;
            favoriteNumbers.Add(num);
        }

        Console.WriteLine("당신이 좋아하는 숫자:");
        foreach (int n in favoriteNumbers)
        {
            Console.Write(n + " ");
        }

        Console.WriteLine("\n첫 번째 숫자를 삭제할까요? (y/n)");
        if (Console.ReadLine().ToLower() == "y")
        {
            favoriteNumbers.RemoveAt(0);
        }

        Console.WriteLine("지금 리스트:");
        foreach (int n in favoriteNumbers)
        {
            Console.Write(n + " ");
        }
    }
}

참고: 여기서는 int.Parse를 썼어. 실제 프로젝트에서는 int.TryParse로 입력값을 꼭 체크해서, 사용자가 잘못 입력해도 프로그램이 안 죽게 하는 게 좋아.

6. 초보자가 자주 하는 실수

이런 경우 진짜 많아: 0부터 List.Count까지 반복문을 돌리면서(foreach 등) 리스트를 동시에 수정(추가/삭제)하는 거. 이러면 안 돼! 프로그래밍 고수들은 이걸 "순회 중 컬렉션 수정"이라고 부르는데, 대부분 바로 InvalidOperationException 예외가 터져.

나쁜 코드 예시:

foreach (int n in numbers)
{
    if (n < 0)
        numbers.Remove(n); // 펑! 런타임 에러.
}

정답: 삭제할 인덱스/요소를 따로 리스트에 모아두거나, for문을 거꾸로 돌려서 삭제해야 해.

2
과제
C# SELF, 레벨 27, 레슨 2
잠금
List에서 문자열 다루기
List에서 문자열 다루기
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION