배열과 컬렉션 기초
값을 여럿 담는 Array 와 List 를 다룹니다.
값을 여럿 담아 두기
학생 서른 명의 점수를 다루려고 변수를 서른 개 만들 수는 없습니다.
배열과 컬렉션은 값을 하나의 이름 아래 여럿 담아
두고, 번호나 순서로 꺼내 쓰게 해 줍니다. 앞 단원의
foreach 가 탐색하던 대상이 바로 이것입니다.
가장 먼저 만나는 두 가지는 이렇게 다릅니다.
-
배열(
int[]) — 만들 때 크기를 정하고 그 뒤로 바꾸지 못합니다. 몇 개인지 이미 알고 있고 그 수가 변하지 않을 때 알맞습니다. -
리스트(
List<int>) — 담고 빼는 대로 크기가 저절로 늘고 줍니다. 몇 개가 될지 모를 때 사용합니다.
둘 다 번호는 0부터 셉니다. 세 개짜리의 번호는 0·1·2 이며, 3을 넣으면 없는 자리를 건드려 실행 중에 예외가 납니다.
배열 만들고 꺼내기
아래 코드는 브라우저 안에서 실제로 실행됩니다. 고쳐서 눌러 보셔도 됩니다.
string[] fruits = { "사과", "바나나", "포도" }; Console.WriteLine(fruits.Length); Console.WriteLine(fruits[0]); fruits[2] = "딸기"; Console.WriteLine(fruits[2]);
코드는 고쳐서 실행해 볼 수 있습니다. 처음 누를 때만 실행기를 내려받느라 잠시 걸립니다. 적은 코드는 서버로 나가지 않습니다.
크기는 못 바꾸지만 안에 담긴 값은 바꿀 수 있습니다. 위에서
fruits[2] 를 딸기로 갈아 끼운 것처럼, 자리는 그대로 두고
그 자리의 값만 바꾸는 것은 됩니다. 반면 네 번째 자리를 새로 만들 수는 없습니다.
배열을 만드는 세 가지 방법
// ① 크기만 정하기 — 숫자는 0, 문자열은 null 로 채워집니다 int[] a = new int[3]; // ② 값을 늘어놓기 — 크기는 값의 수로 정해집니다 int[] b = { 10, 20, 30 }; // ③ 컬렉션 식 (C# 12.0+) int[] c = [10, 20, 30];
②는 선언과 동시에만 됩니다. 이미 만들어 둔 변수에 나중에
b = { 1, 2 }; 라고 적으면 컴파일되지 않습니다. 그때는
new int[] { 1, 2 } 처럼 적거나, C# 12.0부터라면 ③의
대괄호를 사용하면 됩니다.
크기가 변하는 리스트
List<int> 의 꺾쇠 안에는 무엇을 담을지
적습니다. 이렇게 담을 타입을 밖에서 정해 주는 방식을 제네릭이라고 하며, 자세한 것은
3부에서 봅니다. 지금은 List<string> 이 문자열을
담는 리스트라는 정도로 충분합니다.
| 멤버 | 하는 일 | 예시 |
|---|---|---|
| Add | 끝에 하나 더하기 | list.Add("사과"); |
| Insert | 정한 자리에 끼워 넣기 | list.Insert(0, "포도"); |
| Remove | 그 값을 찾아 하나 빼기 | list.Remove("사과"); |
| RemoveAt | 번호로 빼기 | list.RemoveAt(0); |
| Contains | 들어 있는지 묻기 | list.Contains("포도") |
| Count | 지금 몇 개인지 | list.Count |
개수를 묻는 이름이 서로 다릅니다. 배열은
Length, 리스트는 Count 입니다.
자주 헷갈리는 자리이고, 잘못 적으면 컴파일 오류로 걸리므로 그때 바꿔 주면 됩니다.
string[] names = new string[2]; names[0] = "민준"; names[1] = "서연"; // names[2] 는 예외 Console.WriteLine(names.Length);
var names = new List<string>(); names.Add("민준"); names.Add("서연"); names.Add("지호"); // 얼마든지 Console.WriteLine(names.Count);
뒤에서 세기와 잘라 내기
C# 8.0부터는 끝에서부터 번호를 셀 수 있습니다.
^1 이 마지막이며, 개수에서 1을 빼는 계산을 직접 하지
않아도 됩니다. 두 점(..)으로 일부만 잘라 낼 수도 있습니다.
int[] n = { 10, 20, 30, 40, 50 }; n[^1] // 50 — 마지막 n[1..3] // { 20, 30 } — 1번부터 3번 앞까지 n[2..] // { 30, 40, 50 } — 2번부터 끝까지
잘라 내는 범위의 뒤쪽 번호는 포함되지 않습니다.
1..3 은 1번과 2번, 두 개입니다.
for 문의 조건이 < 인 것과
같은 셈법입니다.
이 문법, 몇 버전부터 사용할 수 있나요
- 1.0배열
- 2.0제네릭 — List<T> 가 여기서 나왔습니다
- 3.0컬렉션 이니셜라이저 — new List<int> { 1, 2 }
- 8.0인덱스(^1) · 범위(1..3)
- 12.0컬렉션 식 — [1, 2, 3]
배열은 처음부터 있었지만 List<int> 는
C# 2.0의 제네릭과 함께 나왔습니다. 그 전에는 무엇이든 담기는
ArrayList 를 사용했는데, 꺼낼 때마다 타입을 되돌려
밝혀야 했고 엉뚱한 것이 섞여 있어도 실행 전에 걸리지 않았습니다. 지금 코드에서
ArrayList 를 만난다면 아주 오래된 것입니다.
직접 해보기
int[] scores = { 80, 95, 70, 88 }; 의 평균을 구해
출력해보세요. 결과가 83.25 로 나와야 합니다.
string[] input = { "사과", "포도", "사과", "딸기" };
를 탐색하면서, 아직 담기지 않은 것만
List<string> 에 넣어보세요. 결과는 세 개가
됩니다.
- 배열은 크기가 고정이고, 리스트는 담는 대로 늘고 줍니다.
- 번호는 0부터입니다. 배열의 크기가 3이면 마지막 번호는 2입니다.
- 개수를 묻는 이름이 다릅니다 — 배열은
Length, 리스트는Count. ^1은 마지막,1..3은 1번부터 3번 앞까지입니다.
C# 버전별 변경 이력 에서 각 버전이 무엇을 더했는지 볼 수 있습니다.