이번에는 컬렉션에 대해 알아보겠습니다. 컬렉션은 같은 성격을 띄는 데이터들을 담는 자료구조를 말합니다. 그 중 설명드릴 컨테이너는 ArrayList, Queue, Stack, Hashtable 4가지이며 .NET 프레임워크에서 제공해주는 자료구조입니다.
먼저 ArrayList를 알아보겠습니다. ArrayList는 가장 배열과 닮은 컬렉션이라 할 수 있습니다.
컬렉션의 요소에 접근할 때 [] 연산자를 이용하고, 특정 위치에 있는 요소에 데이터를 임의로 할당할 수도 있습니다. 하지만 배열과 다른점은 컬렉션을 생성할 때 용량을 미리 지정할 필요없이 필요에 따랄 자동으로 공간이 늘어나거나 줄어든답니다.
ArrayList의 큰 장점이기도 하지요.
ArrayList에서 가장 중요한 메서드는 Add(), RemoveAt(), Insert() 입니다.
Add()
|
컬렉션의 가장 마지막에 있는 요소 뒤에 새 요소를 추가합니다.
|
RemoveAt()
|
특정 인덱스에 있는 요소를 제거합니다.
|
Insert()
|
원하는 위치에 새 요소를 삽입합니다.
|
코드로 한번 살펴보겠습니다.
먼저 컬렉션을 사용하기전 네임스페이스 사용을 선언해야 합니다
1
|
using System.Collections;
| cs |
아래 코드는 ArrayList를 생성하고 컬렉션안에 데이터를 추가하고 제거하는 코드입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
ArrayList list = new ArrayList();
list.Add(10);
list.Add(20);
list.Add("string");
list.Add(1.234);
list.RemoveAt(1);
list.Insert(0, "test");
| cs |
먼저, 코드를 보시면 아시겠지만 이전에 사용하던 컬렉션과 다르다는게 보입니다. 저는 C++을 먼저 배우고 C#을 공부했기때문에 상당히 의아했죠, C++에서는 한가지 자료형만 담을 수 있는 반면 C#에서 제공된 컬렉션에서는 모든 타입을 수용할 수 있답니다.
1
2
3
|
public virtual void Insert(int index, object value);
public virtual int Add(object value);
| cs |
ArrayList의 Add와 Insert를 타고 들어가면 매개변수가
object 타입임을 확인할 수 있습니다.
그 이유는 컬렉션 요소는 어떤타입이든지 object타입으로 저장이 되기 때문이라고 합니다. 이 부분에 대해서는 마지막에 다시한번 설명드리겠습니다. 우선 C#컬렉션은 여러 타입을 저장할 수 있다는 점! 을 기억해주시면 되겠습니다.
List 변수의 내부를 보시면 int형도 들어가고, string, float 타입까지 모두 한 컬렉션안에 담을 수 있는것을 확인할 수 있습니다.
두번째로 Queue입니다.
Queue는 데이터나 작업을 차례대로 입력해두었다가 입력된 순서대로 하나씩 꺼내 처리되는 방식입니다. 선입선출이지요.
큐의 입력은 오로지 뒤에서만 가능하고, 출력은 앞에서만 이루진다는 특징이 있습니다.
1.1 Queue의 Enqueue() 구조 |
1.2 Queue의 Dequeue() 구조 |
큐를 사용할때는 두가지 메소드를 기억하시면 됩니다.
Enqueue(object obj)
|
컬렉션의 맨 뒤에 요소를 삽입합니다.
|
Dequeue()
|
컬렉션의 맨 앞의 요소를 꺼냅니다.
|
코드로 먼저 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Queue que = new Queue();
que.Enqueue(1);
que.Enqueue(2);
que.Enqueue(3);
que.Enqueue(4);
que.Dequeue();
| cs |
1부터 차례로 4까지 데이터를 삽입합니다. 그러면 순차적으로 마지막 요소의 뒤에 삽입되는것을 확인하실 수 있습니다.
그리고 Dequeue()메소드를 호출하게되면
맨 앞에 추가되었던 '1' 요소가 제거된것을 확인할 수 있습니다.
세번째로 알아볼 컬렉션은 Stack입니다.
Stack은 먼저 들어온 데이터가 나중에 나가고, 나중에 들어온 데이터가 먼저 나가게 되는 구조입니다. 앞서 설명드렸던 queue와 반대의 특징을 가지지요. 선입후출의 특징입니다
2.1 Stack의 Push() 구조 |
2.2 Stack의 Pop() 구조 |
Push()
|
컬렉션의 맨 위에 요소를 삽입합니다.
|
Pop()
|
컬렉션의 맨 위에 요소를 제거하고 반환합니다.
|
스택에 데이터를 입출력할 때에는 위 두 함수가 사용됩니다. 스택사용은 아래 코드를 보시면,
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
object popValue = stack.Pop();
| cs |
1부터 4까지 데이터가 삽입이 되면, 나중에 들어온 데이터들이 앞에 쌓이는것을 확인하실 수 있습니다.
그리고 Pop을 통해 제거하게되면 가장 마지막에 들어왔던 '4' 데이터를 제거하고 반환하게 되지요.
마지막으로 Hashtable 입니다.
Hashtable은 키(key)와 값(value)이 쌍을 이루는 구조로 되어있습니다. 탐색 속도가 빠르고, 사용하기도 편한 특징을 가지고있지요.
Hashtable의 구조를 예를 들게되면 사전과 흡사합니다 'book'을 key로, '책'을 값으로 입력하는 식이죠.
샘플 코드를 보시면,
1
2
3
4
5
6
7
8
9
|
Hashtable ht = new Hashtable();
ht["book"] = "책";
ht["apple"] = "사과";
ht["home"] = "집";
ht["go"] = "가다";
| cs |
사용방법이 배열과 흡사합니다. 배열과 다른점은 배열은 저장할 요소의 위치로 인덱스를 사용하는 반면, 해시테이블은 키 데이터를 그대로 사용합니다.
1
|
public virtual object this[object key] { get; set; }
| cs |
해쉬테이블에 오버로딩된 []연산자를 보시면 Key와 value역시 object타입을 받기때문에 어떠한 타입이든 무관합니다
해시테이블은 배열에서 인덱스를 이용해서 배열 요소에 접근하는 것에 준하는 탐색 속도를 자랑합니다. 다시 말하면 탐색 속도가
거의 소요되지 않는다고 할 수 있습니다. 배열의 경우 인덱스 위치에 어떤 데이터를 알고있지않는 한, 순차적으로 리스트를 탐색해
찾아야합니다. 하지만 해시테이블은 키를 이용해 단번에 데이터가 저장되어있는 컬렉션 내의 주소를 계산해 냅니다.
이 작업을 해싱(Hasing)이라고 하며 Hashtable의 이름은 이 알고리즘에서 유래한 것이라고 합니다.
4가지 컬렉션에 대해 알아보았는데요, 아까 처음에 말씀드렸다시피 위 컬렉션들은 다양한 데이터 타입을 저장할 수 있습니다.
하지만 여러가지 요소를 담을 수 있다고해서 무분별하게 사용해서는 안됩니다. 컬렉션에 데이터 입출력 함수들을 보시면 공통적으로 object 타입으로 받는것을 보실 수 있습니다.
예를 들어, Add()메소드에 int 형식의 데이터를 넣더라도 정수 형식 그대로 입력되는 것이 아니라 object형식으로 박싱(Boxing)되어 입력되는 것입니다. 다른 데이터타입도 마찬가지겠지요.
반대의 경우 ArrayList의 요소에 접근해서 사용할 때는 원래의 데이터 형식으로 언박싱(Unboxing)이 이루어지게 됩니다.
박싱과 언박싱은 작지 않은 오버헤드를 요구하는 작업이랍니다. 결국 컬렉션에 데이터의 입출력이 있을때, 박싱과 언박싱이 계속 발생하게 되고, 데이터가 많아질수록 성능저하가 야기됩니다.
그래서 이러한 문제때문에 '일반화 컬렉션'을 많이 사용하는데요, 박싱과 언박싱이 일어나지 않으려면, 데이터 타입이 한가지여야겠지요. 이전 시간에 일반화 프로그래밍에서 보셨듯이 컬렉션에 한가지 데이터타입만 받아 관리가 될 수 있도록 만들어진 자료구조입니다.
다음에는 일반화 컬렉션에 대해 알아보겠습니다.