멈추지 않고 끈질기게

[C#] 배열(Array) 본문

C#

[C#] 배열(Array)

sam0308 2023. 1. 20. 11:37

※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.

※ 해당 포스팅은 .Net 5.0 버전을 기준으로 작성되었습니다.

 

모든 프로그래밍 언어에서는 데이터들을 한데 모아 저장할 수 있는 데이터 집합을 제공합니다.

본 포스팅에서는 그 중 가장 기본적인 자료형인 배열(Array)에 대해 알아보겠습니다.

 

1. 배열의 선언과 초기화

배열은 다음과 같이 선언 및 초기화할 수 있습니다.

//선언 후 초기화
int[] arr1;
arr1 = new int[5];

//선언과 동시에 초기화1
int[] arr2 = new int[5];

//선언과 동시에 초기화2
int[] arr3 = { 1, 2, 3, 4, 5 };

//2차원 배열 - 선언 후 초기화
int[,] arr4;
arr4 = new int[5, 5];

//2차원 배열 - 선언과 동시에 초기화1
int[,] arr5 = new int[5, 5];

//2차원 배열 - 선언과 동시에 초기화2
int[,] arr6 = { { 1, 2}, { 3, 4}, { 5, 6} };

(3차원 이상은 2차원과 동일한 방식으로 늘리면 되기에 생략하였습니다.)

 

배열은 초기화 시에 크기를 미리 지정해주어야 합니다. 따라서 저장하려는 데이터의 양이 예측 가능할 때 사용하거나

데이터의 전체 양을 먼저 읽어낸 뒤에 초기화해야 합니다.

 

2. 배열의 속성

배열은 다음과 같은 속성들을 가지고 있습니다.

int[] arr1 = new int[] { 1, 2, 3, 4 };
int[,] arr2 = new int[3, 4];

//배열의 크기(원소의 갯수), int 값
Console.WriteLine($"arr1의 크기 : {arr1.Length}");
Console.WriteLine($"arr2의 크기 : {arr2.Length} \n");
//배열의 크기(원소의 갯수), long 값
Console.WriteLine($"arr1의 크기 : {arr1.LongLength} \n");
//배열의 차원 수, int 값
Console.WriteLine($"arr1의 차원 수 : {arr1.Rank}");
Console.WriteLine($"arr2의 차원 수 : {arr2.Rank}");

그림1. 배열의 속성

Length 값은 배열의 모든 원소의 갯수므로, n * m 사이즈의 2차원 배열일 경우 n * m 을,

i * j * k 사이즈의 3차원 배열일 경우 i * j * k 을 반환합니다. 

n이나 m값을 알아내려면 후기에 기술할 함수를 사용해야 합니다.

 

LongLength 값은 Length와 동일하며 int 대신 long 타입으로 반환합니다.

 

Rank 값은 차원 수를 나타내며 기본 1차원 배열일 경우 1을, n차원 배열일 경우 n을 반환합니다. 

 

이외에 IsFixedSize, IsReadOnly 속성은 각각 항상 참, 거짓이므로 생략하였습니다.

( IsFixedSize : 배열은 고정된 사이즈를 가지므로 항상 true

  IsReadOnly : 배열은 readonly 키워드로 선언할 수 없으므로 항상 false)

 

3. 배열 관련 함수

배열과 관련한 함수들은 System 네임스페이스의 Array 클래스에 포함되어 있습니다.

배열 관련 함수들은 다음과 같습니다.

 

//각 차원의 배열 크기 반환, int 값
int[,] arr2 = new int[3, 4];
Console.WriteLine($"arr2는 {arr2.GetLength(0)}*{arr2.GetLength(1)} 배열");

int[] arr1 = new int[] { 1, 3, 4, 2 };
Console.Write("arr1 : ");
PrintElements(arr1);

//배열 순서 뒤집기
Array.Reverse(arr1);
Console.Write("arr1(리버스) : ");
PrintElements(arr1);

//배열 정렬(오름차순)
Array.Sort(arr1);
Console.Write("arr1(오름차순) : ");
PrintElements(arr1);

//배열 정렬(내림차순)
Array.Sort(arr1, (a, b) => { return (a > b) ? -1 : 1; });
Console.Write("arr1(내림차순) : ");
PrintElements(arr1);

//배열의 원소 지우기
Array.Clear(arr1, 0, 2);
Console.Write("arr1(클리어) : ");
PrintElements(arr1);

//배열 복사하여 붙여넣기
int[] arr3 = { 5, 6, 7, 8 };
Array.Copy(arr3, arr1, 2);
Console.Write("arr1(복사) : ");
PrintElements(arr1);

int[] arr4 = { 4, 5, 4, 5, 4, 5 };
Console.Write("\narr4 : ");
PrintElements(arr4);

//특정 원소 인덱스 찾기
int idx = Array.IndexOf(arr4, 5);
Console.WriteLine($"5의 인덱스 : {idx}");
idx = Array.LastIndexOf(arr4, 5);
Console.WriteLine($"5의 인덱스 : {idx}");

그림2. 배열의 함수

(PrintElements()는 개인적으로 작성한 배열의 모든 원소를 출력하는 함수입니다.)

 

GetLength(int dimension) 함수는 해당 배열의 (dimension) 차원의 크기를 반환합니다.

1차원 배열은 GetLength(0) = Length 이므로 필요없고, 2차원 이상의 배열에서 사용합니다.

주의할 점은 배열의 인덱스와 마찬가지로 0부터 시작하므로, n차원 배열이라면 0 ~ n-1을 매개변수로 주어야 합니다.

n이상의 값을 매개변수로 줄 경우 IndexOutOfRangeException이 발생합니다.

 

 Array.Reverse() 함수는 해당 배열의 원소 순서를 반대로 뒤집습니다.

해당 함수는 1차원 배열에만 사용 가능합니다. 2차원 이상의 배열을 매개변수로 줄 경우, RankException이 발생합니다.

 

Array.Sort() 함수는 해당 배열의 원소를 정렬합니다. 매개변수로 배열만 전달했을 경우 해당 자료형의 기본 정렬 기준으로

정렬됩니다(숫자의 경우 오름차순). 두번째 매개변수로 대리자를 넘길 경우, 해당 조건에 맞추어 정렬할 수 있습니다. 상기 코드에서는 다음 원소가 더 클 경우 1을 반환하는 람다식을 전달하여 내림차순으로 정렬하였습니다.

어떤 대리자를 매개변수로 전달하느냐에 따라서 숫자 외의 데이터도 정렬 가능하며, 조건식을 여러개 중첩하여

정렬 기준을 더 고도화 할 수도 있습니다.

(대리자나 람다식에 대해서는 추후 다른 포스팅에서 자세히 다루도록 하겠습니다.)

 

Array.Clear(Array array, int index, int length) 함수는 해당 배열의 특정 구간을 자료형에 맞는 초기값으로 덮어씁니다.

array 배열의 index 부터 length 길이만큼을 초기값으로 만듭니다. 상기 코드에서는 arr1의 0번째 원소부터 2개,

즉 0, 1번째 원소를 int 형의 초기값인 0으로 만들었습니다.

 

Array.Copy(Array sourceArray, Array targetArray, int length) 함수는 특정 배열의 길이만큼 타겟 배열을 덮어씁니다.

targetArray의 0번째 인덱스부터 sourceArray의 원소로 length 길이만큼 덮어씁니다. 상기 코드에서는 arr1의

0번째 원소부터 2개, 즉 0, 1번째 원소를 arr3의 0, 1번째 원소로 덮어씌웠습니다.

 

Array.IndexOf() 함수는 해당 배열에서 특정 원소의 인덱스 값을 반환합니다. 단, 해당 원소가 배열에 여러개 존재할 경우

가장 낮은 인덱스 값을 반환합니다. Array.LastIndexOf() 함수는 이와 반대로 가장 높은 인덱스 값을 반환합니다. 

상기 코드에서 1, 3, 5번째 원소가 5인 arr4 배열에서 5의 인덱스를 검색 시 IndexOf() 함수를 사용했을 때는 1을, LastIndexOf() 함수를 사용하였을 때는 5를 반환하는 것을 알 수 있습니다.

 

4.배열의 장단점

배열의 가장 큰 장점은 인덱스를 통한 랜덤엑세스가 가능하다는 점입니다. 특정 원소값에 접근할 때 그 인덱스를

알고 있다면 O(1)의 시간복잡도로 접근할 수 있습니다.

또한 인덱스를 통해 반복문을 돌며 저장된 모든 데이터에 순차적으로 접근하는 것도 가능합니다.

 

배열의 단점은 크기가 고정적이라는 점입니다. 초기화시에 크기를 지정해주어야 하기 때문에, 저장해야 하는 데이터의

갯수를 미리 판단하기 어려운 상황에서는 다소 사용하기 불편합니다. 불가능한 것은 아니지만, 이미 모든 원소에 값을 할당한 배열에 데이터를 추가하려면 배열로 만들어 기존의 배열 뒤에 붙이는 등 다소 번잡한 과정을 거쳐야 합니다.

또한 필요 없어진 데이터의 경우 초기값으로 덮어씌울 수는 있지만, 배열에서 완전히 제거할수는 없습니다.

 

C#에는 인덱스를 통한 랜덤액세스를 제공하면서 동적으로 크기를 조절할 수 있는 List 가 존재하는데,

이는 다음 포스팅에서 알아보도록 하겠습니다.

'C#' 카테고리의 다른 글

[C#] 대리자(Delegate)  (0) 2023.02.09
[C#] IComparable 인터페이스, Comparison<T> 대리자  (0) 2023.01.31
[C#] 딕셔너리(Dictionary)  (0) 2023.01.27
[C#] 큐(Queue)와 스택(Stack)  (0) 2023.01.26
[C#] 리스트(List)  (0) 2023.01.25