Programming language/C#

[C#]컬렉션(Collection)

CodingGom 2022. 4. 19. 23:55

서론

C++에 STL이 있다면, C#에서는 데이터 원소들을 저장, 관리하는 컬렉션이 있습니다. 이번 글에서는 C#의 데이터 저장과 처리를 위한 자료구조 컬렉션에 대해서 다뤄보려 합니다.


제네릭/논제네릭 컬렉션 (Generic/Non-Generic Collection)

 이전에 [C#] 제네릭(Generic)에 관한 글을 쓴 적이 있습니다. 컬렉션을 알기 전에, 컬렉션에 두 가지 타입이 있다는 걸 먼저 말하고 싶습니다. 컬렉션이 Generic/Non-Genrice이냐는 컬렉션에 담기는 데이터(원소) 타입이 명시적 인지 아닌지에 따라 다릅니다.

 

 다른 부가적인 사항들 말고, 핵심적인 것 하나만 알면 됩니다. 데이터의 타입이 명시적이냐는 겁니다. 아래 예시를 보겠습니다.

 

//Generic collection : System.Collections.Generic.List<T>
List<int> list = new List<int>(); // int 타입의 데이터를 담을 수 있는 동적 배열 기반의 컬렉션
list.Add(10); //ok
list.Add("ten"); //error

//Non-Generic collection : System.Collections.ArraryList
ArraryList arraryList = new ArraryList(); //object 타입의 데이터를 담을 수 있는 동적 배열 기반의 컬렉션
arraryList.Add(10); //ok
arraryList.Add("ten"); //ok

 List <T>는 제네릭 컬렉션입니다. 제네릭 타입으로 T표식이 달려 있다는 것은, 어떤 타입인지 모르지만 한 가지 타입만을 명시적으로 받겠다는 뜻이죠. 선언 시 타입만 정해주면, 어떤 타입이든 상관없지만 오로지 그 타입만을 처리해야 합니다.

 

 반대로 논제네릭 컬렉션인 ArraryList는 특정 타입이 명시적으로 정해져 있지 않습니다. 즉 object타입을 받는다는 뜻이며, 하나의 컬렉션에 여러 타입의 원소를 담을 수 있다는 뜻이죠. 이는 원소를 object 타입으로 받아(Boxing)서 사용할 때 명시적으로 캐스팅해(Unboxing) 사용할 수 있기 때문입니다.

 

 이 외에 사실상 ArraryList와 List의 경우처럼 같은 자료구조의 컬렉션이지만 제네릭/논제네릭으로만 나뉘기에 필요에 따라 사용하면 됩니다. 이후에 몇 가지 주요 컬렉션 종류를 알아보겠습니다.


컬렉션 몇가지 종류

Generic collection Non-Generic collection Data struct
List<T> Arrary, ArraryList 동적 배열, 랜덤 접근 가능
Dictionary<TKey, TValue>
Hashtable 해시 테이블, 랜덤 접근 가능
Queue<T> Queue 큐, FIFO
Stack<T> Stack 스택, LIFO
SortedList<TKey, TValue> SortedList Map ADT(Abstract data type), 배열
HashSet<T>, SortedSet<T> - 집합(Set), 정렬 여부 차이
LinkedList<T> - 연결 리스트

 기반 자료구조가 동일하고 제네릭 컬렉션 중, 논제네릭을 지원하는지 여부의 차이만 있을 뿐 큰 차이점은 없다고 보면 됩니다. 전부를 나열한 것은 아니지만 많이 보이는 컬렉션들을 소개해 봤습니다.


마무리

 컬렉션과 기반 자료구조와 제네릭의 여부를 아는게 중요한 것 같습니다. 한 가지 예를 들어 보겠습니다. 다음과 같은 상황에서는 어떤 컬렉션을 선택하는 게 좋을까요?

 

  1. 처리해야 할 원소의 데이터 타입은 선언시 명시적으로 정합니다.
  2. 순차적으로 탐색하는 경우도 종종 있고, 간혹 랜덤 액세스가 필요합니다.
  3. 데이터의 중복을 허용합니다.
  4. 삽입과 삭제는 빈번하게 일어나지 않습니다.

각 항목을 하나하나 살펴보겠습니다.

  1. 처리해야 할 원소의 데이터 타입은 선언 시 명시적으로 정합니다.
    => 기능은 일반화가 되어 있지만, 타입이 미정인 상태입니다. 사용자가 사용할 때(선언 시) 정해주면 되므로, 제네릭 타입의 컬렉션 중 선택합니다.
  2. 순차적으로 탐색하는 경우도 종종 있고, 간혹 랜덤 액세스가 필요합니다.
    => 랜덤 액세스가 되어야 하고, 순차 탐색이 가능해야 합니다.
  3. 데이터의 중복을 허용합니다.
    => 정렬된 상태나, 데이터의 중복을 할 수 없는 컬렉션은 제거합니다.
  4. 삽입과 삭제는 빈번하게 일어나지 않습니다.
    => 컬렉션 중 삽입과 삭제에 드는 시간 복잡도가 다릅니다. 빈번하지 않으므로 크게 고려하지 않아도 됩니다.

 살펴본 결과 사용 하기에 적절한 컬렉션은 List <T>, Dictionary <TKey, TValue> 정도가 있겠네요. 어디까지나 예시입니다. 이후에 여러 가지를 고려해 볼 수 있습니다. 몇 가지 예시를 보겠습니다.

 

  •  랜덤 액세스를 인덱스로 할 것인지, 유니크한 key 값으로 할 것인지?
    => 구현 복잡도와 가독성 등 코드 스타일과 설계 목적에 맞게 선택하면 될 것입니다.
  • 순차적으로 탐색할 때, Dictionary의 경우는 일반 for문 말고 foreach를 많이 사용합니다.
    => foreach는 가비지가 생기는 작업입니다. 부하가 있을 수 있겠지만, 추가 작업으로 for문 사용도 가능합니다.
  • 삽입과 삭제가 빈번하지는 않지만 삭제 시에는 Dictionary가 O(1)로 List의 O(n)보다는 빠릅니다.
    => 삭제가 빈번할 수 있는 확장성이 고려된다면, 생각해 볼 수 있겠네요.

 이처럼 딱 정해진 답은 없습니다. 하지만 어떤 상황이냐에 적절한 선택을 할 수 있는지는 얼마나 알고 있냐가 큰 관건인 것 같습니다.

반응형
LIST