목표
- Collections 사용법에 대해 알아보자.
Collection
- 프로그램을 제작하다보면 대부분 개채의 그룹을 만들려고 할 것이다. (총알이라던가, 가방, 사용자 목록...)
- 개체를 그룹화 하는 방법은 두가지가 있다.
- 크기가 정해져있는 배열을 사용하는 방법
- 가변적인 크기를 가지는 콜렉션을 사용하는 방법
Collection의 종류
- Collction을 사용하기 위해서는 System.Collections.Generic를 포함해야 한다.
- 다음은 자주 사용되는 Collection에 대한 설명이다.
클래스 |
설명 |
Dictionary<TKey, TValue> |
Key에 따라 Value에 접근할 수 있는 자료형 |
List |
인덱스로 Value에 접근할 수 있는 자료형 |
Queue |
FIFO(First In First Out) 방식의 자료형 |
Stack<TKey, TValue> |
IComparer 구현을 기반으로 정렬된 Key/Value를 관리하는 자료형 |
SortedList |
LIFO(Last In First Out) 방식의 자료형 |
List
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
// 개체 이니셜라이저를 사용하여 개체의 이니셜라이저를 설정할 수 있다.
List<string> userList = new List<string>()
{
"white", "black"
} ;
// Start is called before the first frame update
void Start()
{
// Add를 사용하여, List의 마지막에 값을 추가할 수 있다.
userList.Add("red");
userList.Add("blue");
userList.Add("greend");
// Count를 사용하여 List의 길이를 알 수있다.
// for문을 사용한 리스트 값 확인 방법
for (int i = 0; i < userList.Count; i++)
{
// 인덱스로 List 요소에 접근할 수 있다.
Debug.Log(string.Format("{0} : {1}", i, userList[i]));
}
// Instert를 사용하여 특정 위치에 값을 삽입할 수 있다.
userList.Insert(0, "yellow");
// foreach문을 사용한 리스트 값 확인 방법
int index = 0;
foreach (var user in userList)
{
Debug.Log(string.Format("{0} : {1}", index, user));
index++;
}
// enumerator를 사용한 리스트 값 확인 방법
index = 0;
IEnumerator enumerator = userList.GetEnumerator();
while (enumerator.MoveNext() && enumerator.Current != null)
{
Debug.Log(string.Format("{0} : {1}", index, enumerator.Current));
index++;
}
// 특정 값 제거
userList.Remove("red");
// 특정 위치의 요소를 제거한다.
userList.RemoveAt(0);
// 특정 위치부터, n개의 요소를 제거한다.
userList.RemoveRange(0, 2);
// 특정 조건의 값을 삭제한다.
userList.RemoveAll(userId =>
// userId가 "b"로 시작한다면 true 리턴
userId.StartsWith("b", System.StringComparison.Ordinal)
);
// 리스트 값 모두 제거.
userList.Clear();
}
}
Dictionary
- 주의할 점은 이미 추가된 key값과 동일한 key로는 값을 할당할 수 없다.
- 아주 가끔 이걸 엔진 버그라고 주장하시는 글이 게임 개발 커뮤니티에 올라오기도 하지만, 언어 스펙이다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
// 개체 이니셜라이저를 사용하여 초기화 할 수있다.
Dictionary<string, string> urlList = new Dictionary<string, string>()
{
{"nhn", "www.nhn.com"},
{"naver", "www.naver.com"},
{"kakao", "www.kakao.com"}
};
// Start is called before the first frame update
void Start()
{
// Add를 사용하여 값을 넣을 수 있다.
urlList.Add("daum", "www.daum.com");
// 특정 키가 이미 포함되어 있는지 알 수 있다.
if (urlList.ContainsKey("nhn"))
{
Debug.Log("contain nhn key!!");
}
// 특정 값이 포함되어 있는지 알 수 있다.
if (urlList.ContainsValue("www.nhn.com"))
{
Debug.Log("contain nhn value!!");
}
// 키를 기준으로 특정 값을 제거할 수 있다.
urlList.Remove("naver");
// Dictionary의 Keys로 요소에 접근할 수 있다.
foreach (var key in urlList.Keys)
{
Debug.Log(string.Format("key : {0}, value : {1}", key, urlList[key]));
}
// 이미 포함되어 있는 키값으로는 값을 삽입할 수 없다.
// ArgumentException : same key has already been added.
urlList.Add("daum", "www.dadaum.com");
}
// Update is called once per frame
void Update()
{
}
}
Queue
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
// LogObject 클래스
public class LogObject
{
public string LogMessage { get; set; }
public string LogLevel { get; set; }
public override string ToString()
{
return string.Format("[{0}] {1}", LogLevel, LogMessage);
}
}
Queue<LogObject> messageQueue = new Queue<LogObject>();
// Start is called before the first frame update
void Start()
{
// Enqueue를 사용하여 Queue에 메세지 삽입
SendMessage("Login", "INFO");
SendMessage("Invalid Password", "CRASH");
SendMessage("Logout", "INFO");
// Peek는 메세지를 Queue에서 빼지 않고, 확인만 한다.
ShowMessageList();
// 배열 형태로 변환도 가능하다.
Queue<LogObject> otherQueue = new Queue<LogObject>();
LogObject[] logs = messageQueue.ToArray();
// Dequeue를 사용하면 FIFO(메세지를 넣은 순서대로 값이 반환된다.)
List<LogObject> logObjects = GetMessage();
for (int i = 0; i < logObjects.Count; i++)
{
Debug.Log("logObjects : " + logObjects[i]);
}
}
void SendMessage(string message, string logLevel)
{
LogObject log = new LogObject();
log.LogMessage = message;
log.LogLevel = logLevel;
messageQueue.Enqueue(log);
}
List<LogObject> GetMessage()
{
List<LogObject> logList = new List<LogObject>();
int queueSize = messageQueue.Count;
for (int i = 0; i < queueSize; i++)
{
LogObject log = messageQueue.Dequeue();
logList.Add(log);
}
return logList;
}
void ShowMessageList()
{
int queueSize = messageQueue.Count;
for (int i = 0; i < queueSize; i++)
{
LogObject log = messageQueue.Peek();
Debug.Log(log);
}
}
}
Stack
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
Stack stack = new Stack();
// Start is called before the first frame update
void Start()
{
stack.Push(1);
stack.Push(2);
stack.Push(3);
foreach(var v in stack)
{
Debug.Log(v);
}
//output : 3, 2, 1
}
}
SortedList
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
SortedList scoreBoard = new SortedList();
// Start is called before the first frame update
void Start()
{
scoreBoard.Add(15, "minjaeKim");
scoreBoard.Add(25, "playerA");
scoreBoard.Add(5, "playerB");
scoreBoard.Add(-5, "playerC");
foreach (var key in scoreBoard.Keys)
{
// playerC : -5
// playerB : 5
// minjaeKim : 15
// playerA : 25
Debug.Log(scoreBoard[key] + " : " + key);
}
}
}
Sort
- 대부분의 Collection에 구현되어 있는 Interface로 값을 정렬하는 기능이 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
List<int> score = new List<int>();
// Start is called before the first frame update
void Start()
{
score.Add(58);
score.Add(75);
score.Add(33);
// 기본 정렬
score.Sort();
// 오름 차순 정렬
score.Sort((a, b) => a.CompareTo(b));
// 내림 차순 정렬
score.Sort((a, b) => b.CompareTo(a));
// 정렬 반전
score.Reverse();
// 출력
enumerator = score.GetEnumerator();
while (enumerator.MoveNext() && enumerator.Current != null)
{
Debug.Log(enumerator.Current);
}
}
}
- 기본으로 정의된 정렬기능 외에 커스텀 정렬 기능을 사용하기 위해서는 IComparer 혹은 IComparable 인터페이스를 사용한다.
IComparer
- 개채 2개를 파라미터로 받아 비교할때 사용한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class CollectionData : IComparable
{
public int Score { get; set; }
public int CompareTo(CollectionData other)
{
// 0 : 두 값이 같다.
// 1 : 비교 대상보다 큰 값을 가지고 있다.
// -1 : 비교 대상보다 작은 값을 가지고 있다.
if(Score == other.Score)
{
return 0;
}
else if (Score > other.Score)
{
return 1;
}
else
{
return -1;
}
}
}
IComparable
- 나 자신과 입력받은 객체를 비교할때 사용한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ComparClass : Comparer
{
public override int Compare(CollectionData x, CollectionData y)
{
// 0 : 두 값이 같다.
// 1 : 비교 대상보다 큰 값을 가지고 있다.
// -1 : 비교 대상보다 작은 값을 가지고 있다.
if (x.Score == y.Score)
{
return 0;
}
else if (x.Score > y.Score)
{
return 1;
}
else
{
return -1;
}
}
}
예제
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleScripts : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
CollectionData c1 = new CollectionData();
CollectionData c2 = new CollectionData();
c1.Score = 10;
c2.Score = 20;
int result = c1.CompareTo(c2);
ComparClass comparClass = new ComparClass();
result = comparClass.Compare(c1, c2);
}
}
참고
- 간혹 Collection 순환을 위해 GetEnumerator을 받고난 뒤, 값을 추가하려는 경우가(?) 있을 수 있다.
- 에러가 발생한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
List<int> score = new List<int>();
// Start is called before the first frame update
void Start()
{
score.Add(58);
score.Add(75);
score.Add(33);
IEnumerator enumerator = score.GetEnumerator();
// 에러가 발생한다.
score.Add(55);
while (enumerator.MoveNext() && enumerator.Current != null)
{
Debug.Log(enumerator.Current);
}
}
}
- 혹은 foreach로 순환하면서, 값을 삭제하려는 사람도 있다.
- 에러가 발생한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectionData : MonoBehaviour
{
Dictionary<int, int> dataDic = new Dictionary<int, int>();
// Start is called before the first frame update
void Start()
{
dataDic.Add(0, 58);
dataDic.Add(1, 75);
dataDic.Add(2, 33);
foreach (var key in dataDic.Keys)
{
if (key == 1)
{
// 에러가 발생한다.
dataDic.Remove(key);
}
}
}
}