지금까지 유니티를 사용하면서 여러 자료구조를 사용해서 개발해왔지만 한번도 이걸 정리해서 글로 써본적도 없이 대충 개념만 파악했는데 이번 기회에 제대로 정리해서 좀 더 이유를 알고 자료구조를 쓰려고 한다
먼저 첫 번째로 배열에 대해 알아보자
동일한 타입의 변수들을 저장할 수 있는 자료구조
메모리 공간에 연속적으로 데이터를 저장하기 때문에 메모리 사용을 최적화할 수 있고 배열의 첫 번째 요소의 주소와 각 데이터 타입의 크기만 알면, 특정 요소에 접근할 수 있다
배열의 각 요소는 특정 인덱스로 참조할 수 있는데 이를 이용해 반복문 등을 이용한 순차 접근과 특정 인덱스를 이용한 임의 접근이 모두 가능하다(배열의 크기를 벗어나는 인덱스로 참조하면 에러)
1차원 뿐만 아니라 2차원, 3차원까지 다차원 배열을 사용해 표현할 수 있어 이미지 처리, 그래픽 등에서 계산에 유용하게 쓰인다
유니티에서는 System.Array가 흔히 알고 있는 배열을 가리킨다
// 선언 : dataType[] arrayName;
int[] intArray; // 초기화는 안 된 상태(null), 이대로 intArray를 참조하면 에러
// 초기화 : 배열 이름 = new 데이터타입[크기]로 초기화해주는데 이때 배열의 크기가 고정된다
intArray = new int[5];
// 선언 + 초기화
int[] intArray = new int[10];
배열을 위와 같이 초기화하면 초기에 들어있는 값은 무엇일까?
for (int i = 0; i < 5; i++)
Debug.Log(intArray[i]);

for(int i = 0; i < intArray.Length; i++)
intArray[i] = i;Array.Fill(intArray, 99);intArray = new int[]{1, 2, 3, 4, 5, 6};
// int[] intArray = {1, 2, 3, 4, 5, 6};
반복문 등에서 배열의 크기를 구하고 싶을 때 [배열명].Length를 통해 배열의 길이를 얻어온다
Debug.Log(intArray.Length);
// 5
한 배열을 다른 배열에 복사하고 싶을 때 사용하는 것은 Array.Copy 함수이다
int intArray = new int[]{1, 2, 3, 4, 5};
int intArray2 = new int[5];
Array.Copy(intArray, intArray2, intArray2.Length);
for (int i = 0; i < intArray2.Length; i++)
Debug.Log(intArray2[i]);
// 1 2 3 4 5
사이즈를 얼만큼 복사할지 3번째 매개변수로 나타낼때 만약 위에서 4를 입력하면 1, 2, 3, 4, 0이 출력된다
자료구조를 사용하다보면 정렬이 꼭 필요할 때가 온다. 그럴 때를 대비해 미리 연습해두자
숫자형 타입인 int, float 등은 정렬할 때 알아서 오름차순으로 정렬이 된다
int[] intArray2 = { 4, 2, 3, 1, 5 };
Array.Sort(intArray2);
for (int i = 0; i < intArray2.Length; i++)
Debug.Log(intArray2[i]);
// 기본적으로 오름차순 정렬이되어 1 2 3 4 5 출력
// 내림차순은 뒤에 나올 Reverse를 통해 정렬한 다음 뒤집어주기
근데 개발하다보면 정렬을 숫자형 타입을 하는 일은 많지 않고 사용자가 정의한 클래스를 정렬해야 할 때가 온다. 클래스에 있는 특정 변수의 값을 기준으로 정렬하는 방법이 있는데 찾아본 바로 방법이 두 가지이다
ICompare<T> 인터페이스를 상속받아 정렬 기준 함수를 선언하기 내가 실제로 프로젝트에서 사용한 예제인데 다음과 같이 정렬했다
Array.Sort(allyFormation, (character1, character2) => {
if(character1 == null && character2 == null)
return 0;
else if(character1 == null)
return 1;
else if(character2 == null)
return -1;
return character1.RowOrder.CompareTo(character2.RowOrder);
});
캐릭터 클래스들이 있는 배열에서 RowOrder 값을 기준으로 정렬하는 람다 함수인데
사용자 클래스가 하나라도 null일 때 RowOrder 값을 비교하면 null 참조 에러가 나기 때문에 앞에서 조건문으로 null인 상황을 예외처리 해줘야 한다
캐릭터 두 개가 다 null이면 동등을 뜻하는 0을 반환하고 양수 값(1)을 반환하면 character2가 character1보다 앞에 있어야 함을 나타내고 음수 값을 반환하면 character1이 앞에 있어야 됨을 나타낸다
그 결과 null인 인스턴스들은 모두 뒤로 밀려나고 null이 아닌 인스턴스들은 RowOrder 값을 기준으로 오름차순 정렬된다
배열의 모든 요소들을 뒤집어서 순서를 바꾼다
Array.Reverse(intArray);
배열의 특정 인덱스를 반환한다. 만약 요소가 없다면 -1을 반환함
int[] intArray = {1, 2, 3, 4, 5};
Debug.Log(Array.IndexOf(6); // 6번째 인덱스 값을 불러오기
// 배열의 크기가 5이니까 6번째 인덱스 값은 없다. 그러므로 -1을 반환
배열 참조 시 null 에러를 방지하기 좋은 함수
배열의 모든 인덱스를 기본값으로 초기화하는 함수
Clear(Array array, int index, int length);
array 배열의 index부터 시작해 index + length까지 기본값으로 초기화시킨다. index + length가 array의 length보다 크면 에러
배열의 크기를 변경하는 함수. 새로운 배열을 생성해 기존 배열의 요소를 복사한 후 그 배열 반환
Array.Resize(ref intArray2, 10);
Debug.Log(intArray2.Length); // 10
크기가 변경되고 나머지 부분은 기본값인 0으로 채워짐
배열 내 특정 조건을 만족하는 요소가 있는 지 확인
int[] intArray = {1, 2, 3, 4, 5};
Debug.Log(Array.Exists(intArray2, value => value == 3)); // true
두 함수 모두 배열 내 특정 조건을 만족하는 요소를 찾는 함수
Find 함수는 조건을 만족하는 첫 번째 요소를 찾아 반환하고 FindAll은 조건을 만족하는 모든 요소를 배열로 만들어 반환한다
Debug.Log(Array.Find(intArray2, value => value >= 3)); // 3
Debug.Log(Array.FindAll(intArray2, value => value > 3).Length); // 2
정렬된 배열에서 사용자가 이진 검색을 따로 구현할 필요 없이 이진 검색 알고리즘을 사용할 수 있는 함수이다. 정렬되어 있다는 가정하에 사용하면 순차 검색보다 좋은 성능을 만들 수 있을 것 같다
Debug.Log(Array.BinarySearch(intArray2, 4)); // 3
{1, 2, 3, 4, 5}에서 4에 해당하는 인덱스를 반환한다
for 문 등을 작성할 필요없이 배열의 각 요소를 순회하면서 특정 작업을 할 수 있는 함수
보통 람다문을 이용해 간단한 작업을 거침
Array.ForEach(intArray2, value => value++);
이렇게 하고 intArray2를 출력해보면 어떻게 될까?
아무런 변화없이 1, 2, 3, 4, 5가 출력된다. 그 이유는 foreach가 배열의 복사본을 사용하기 때문이다. 값의 변경 없이 무언가를 수행하는 함수만 실행할 때 써보자