여러 자료형들을 관리함에 있어, 우리는 다양한 자료형으로 배열을 선언하고 관리하였다.
using System;
namespace Favor
{
internal class Program
{
static void Main(string[] args)
{
int[] iArr = new int[4];
}
}
}
그런데 무언가 낯익은 형태가 보인다.
new로 인스턴스 하는 모습이 꼭 클래스를 선언할 때와 닮아있지 않은가?
물론 new 키워드를 통한 선언이 모두 클래스이진 않지만, 그것을 얘기하려는 것은 아니다.
바로 우리가 사용하던 이 배열조차도, 기본 자료형이 아니라 클래스로 구현되어있다는 것이다.
string만 그런 것이 아니라 배열 역시 클래스였던 것이다.
이쯤 되면 다른 기본 자료형도 혹시...? 싶은 생각이 들 정도다.
아무튼 이 배열 역시 Array라는 이름의 클래스로 구현이 되어있다.
이 말은 즉 string때와 동일하게, Array클래스의 빌트인 메서드를 사용하여 사용자가 직접 알고리즘이나 함수를 구현하지 않고 편리하게 배열을 관리할 수 있다는 것이다.
또, string과 동일하게 배열도 클래스이기 때문에 참조형식으로 이루어져있다. 그러나 이 경우에는, 배열을 복사하고자 할 때도 얕은 복사가 일어난다.
하지만 앞서 설명했던 빌트인 메서드에 깊은 복사를 쉽게 할 수 있도록 도와주는 기능 등 여러가지가 탑재되어있기 때문에 배열의 관리 제어가 좀 더 편해진다.
Array클래스의 빌트인 메서드는 종류가 굉장히 다양해서 전부 다룰 수는 없지만, 유용한 일부 기능에 대해 알아보겠다.
먼저, 가장 자주 사용했던 프로퍼티인 Length이다.
Array.Length는 해당 배열의 크기를 반환해준다.
위의 iArr에 iArr.Length로 배열의 크기인 4에 접근할 수 있다.
다음으로, CopyTo다.
앞서 설명했듯, 배열은 참조형식이기 때문에 빈 배열에 어떤 배열을 대입하려고 하면 얕은 복사가 일어난다.
같은 구조를 가진 또 하나의 배열을 만들고, 원본과 복사본 배열의 값을 서로 다르게 제어하고 싶다면, 얕은 복사가 일어나면 안된다.
얕은복사가 일어난 두 참조형식은 값을 바꾸고자하면 서로의 값이동일하게 바뀌기 때문이다.
이러한 점을 제어하기 위해, 깊은 복사를 통해 배열을 복사하도록 구현한 메서드가 바로 CopyTo다.
using System;
namespace Favor
{
internal class Program
{
static void Main(string[] args)
{
int[] sArr = {1,2,3,4};
int[] dArr = new int[4];
sArr.CopyTo(dArr, 0);
foreach(int i in dArr)
Console.Write(i);
// 출력값 : 1234
}
}
}
CopyTo는 원본의 배열에 참조를 통해 접근하며, 매개변수로 전달받을 배열변수, 복사를 시작할 인덱스 값을 넣어주면 된다.
여기서 전달받을 배열은 원본 배열과 자료형이 같아야하며, 0을 넣으면 처음부터 끝까지, 2를 넣으면 2번 인덱스부터 복사가 진행된다.
Array클래스는 동적 배열이다. 즉, 유동적으로 배열의 크기를 관리할 수 있다는 얘기다.
이를 가능하게 만들어주는 메서드가 바로 Resize다.
Resize는 객체의 기능이 아닌, Array 클래스 본연의 기능이다.
즉, 객체를 통해 접근하는 것이 아닌, Array를 통한 참조를 통해 지정해야한다.
using System;
namespace Favor
{
internal class Program
{
static void Main(string[] args)
{
int[] arr = {1, 2, 3, 4, 5, 6 };
Array.Resize(ref arr, 10);
Console.WriteLine(arr.Length);
// 출력값 : 10
}
}
}
위처럼 Array. 을 통한 참조로 접근하며, 매개로는 크기를 재조정할 배열의 ref. 즉, 주소를 넘겨줘야한다.
이는 포인터나, c++의 &레퍼런스 타입과 비슷하다.
그 다음 재조정하길 원하는 배열의 크기를 넣어주면 위와같이 arr 배열의 길이가 10으로 재조정됐음을 알 수 있다.
다음으로는 Sort다. Sort도 마찬가지로 객체가 아닌 클래스 자체의 기능이므로 Array.를 통해 접근해야 한다.
Sort는 말 그대로 정렬을 도와주는 기능이다. 배열의 모든 요소에 접근하여 인덱스 0부터 끝 인덱스 까지 배열의 값을 오름차순으로 정렬해준다.
배열을 관리하면서, 특정 값부터 특정 값까지를 없애는. 즉, 청소하고싶을 때가 있다.
예를들어, int 배열 arr에서 첫번째 인덱스부터 중간까지를 날려버리고 싶을 때 말이다.
그럴 땐, Clear 기능을 통해 해당하는 값을 0으로 초기화시킬 수 있다.
using System;
namespace Favor
{
internal class Program
{
static void Main(string[] args)
{
int[] arr = { 9, 2, 4, 1, 6, 1, 2, 3};
Array.Clear(arr, 0, 4);
foreach(int i in arr)
Console.Write(i);
// 출력값 : 00000123
}
}
}
이 역시도 Array클래스의 기능이므로, Array.를 통해 접근한다.
매개로는 청소하고싶은 배열, 청소를 시작할 인덱스, 청소를 멈출 인덱스를 정한다.
위의 경우에는, arr 배열의 0번인덱스부터 4번 인덱스. 즉, 9 ~ 1 만큼을 지울 것이다.
Clear를 실행한 후에는, 해당하는 값이 0으로 초기화된다.
이 정도까지 다루고, 다룬 것들을 활용하는 실습을 해보자.
0부터 10까지의 정수가 하나씩 담긴 크기 10의 arr배열이 마구잡이로 섞여있다.
이 때, 나는 arr배열에서 5 이하의 값을 모두 없애고, 10부터 6으로 내려가는 내림차순 배열로 만들고싶다.
물론 배열 안에 0이라는 값이 들어있지 않고, 크기가 딱 맞는 배열로 만들고자 한다.
0의 갯수를 파악하기 위해, 0을 담는 배열도 만들고자 한다.
이에 대한 예제를 풀이해보도록 하겠다.
using System;
namespace Favor
{
internal class Program
{
static void Main(string[] args)
{
int[] arr = { 4,0,2,5,3,7,9,8,1,6,10 };
int[] zeroes;
int[] numbers;
Array.Sort(arr, 0, arr.Length); // 오름차순 정렬
Array.Clear(arr, 0, arr.Length / 2); // 0부터 5까지 0으로 초기화
arr.CopyTo(numbers, arr.Length / 2); // 6부터 10까지의 배열 복사
Array.Reverse(arr); // 배열 순서 반전 (내림차순 정렬)
arr.CopyTo(zeroes, 0, arr.Length / 2); // 0배열 복사
foreach(int i in numbers)
Console.Write($"{i} ");
Console.WriteLine();
foreach(int i in zeroes)
Console.WriteLine($"{i} ");
// 출력값 : 10 9 8 7 6
// 0 0 0 0 0
}
}
}
위의 코드처럼 10부터 6까지의 내림차순 배열과, 0이 담긴 배열이 복사되었다.
억지스러운 예제긴 하지만, 어떤 식으로 사용될 수 있는지를 보여주기 위한 예시였다.
여기서 다루지 않았던 Reverse라는 기능이 나오는데, 이는 배열의 순서를 역전시키는 것이다.
여러 자료형들을 다룰 때, 배열 만큼 유용한 자료구조가 없다. 물론 추후에 더 많은 자료구조를 다룰 것이지만, 가장 근본적인 관리법은 배열을 통한 관리법이다.
기초가 되는 개념의 정의를 확실히 이해하고 활용한다면, 이후에 있을 다른 자료구조의 관리 방법도 쉽게 익힐 수 있을 것이다.
'C# 일기' 카테고리의 다른 글
| 16. 일반화 (Generic <T>) (0) | 2024.03.08 |
|---|---|
| 15. 연산자 오버로딩 (0) | 2024.03.08 |
| 13. String과 StringBuilder (2) | 2024.03.08 |
| 12. 객체지향의 특징 (0) | 2024.03.06 |
| 11. 인터페이스 (Interface) (2) | 2024.03.06 |