[HCI] 7주차 컬렉션

정수현·2025년 4월 25일

2025-04-25


📍 자료구조

  • 자료들이 저장되고 사용되는 방식
  • 정해진 방식에 따라 자료를 저장하고, 자료간 관계를 저장하고, 이를 효과적으로 사용할 수 있는 방법을 정한다.
  • 자료가 많을 때 관리가 용이하다.

🍀 자료 저장 방식

  1. 자료를 순서대로 저장
    → 배열(Array) | 리스트(List)
    자료를 순서에 따라 연결하는 구조
    인덱스로 자료를 읽을 수 있다.
    ArrayList Stack Queue LinkedList<T> List<T> Stack<T> Queue<T>

  2. 키(Key) - 값(Value)
    → 맵(Map) | 딕셔너리(Dictionary)
    고유 Key에 대응하는 자료가 연동되는 구조
    키가 서로 중복되지 않아야 한다.
    키를 인덱스로 삼아 자료를 읽는다.
    Dictionary<T,V> SortedList<T,V> KeyValuePair<T,V>

  3. 집합구조
    → 집합(Set)
    자료를 순서 없이 저장한다.
    동일한 자료는 한 개만 존재하고, 중복 허용 하지 않는다.
    수학의 집합과 유사하다.
    HashSet<T> SortedSet<T>

  4. 계층형
    → 트리(Tree) | 그래프(Graph)
    자료가 하위자료를 갖는 계층적 구조
    연결 경로에 따라 자료를 저장하고 이동하여 읽을 수 있다.


자료구조의 필요성

  • 관련 자료들을 동일한 변수 이름으로 체계적으로 저장하고 사용할 수 있다.
  • 배열을 이용해서 같은 변수 이름으로 여러 개의 자료를 저장하고 인덱스를 통해 각 자료에 접근할 수 있다.
    ⇒ 여러 개의 변수 대신 배열과 반복문을 이용하면 코드를 줄일 수 있다.

🍀 자료구조 예시 1

int x0 = 1;
int x1 = 2;
int x2 = 3;
int x3 = 4;
int x4 = 5;

int[] x = new int[5];

for(int i=0; i<5; i++)
{
	x[i] = i + 1;
}

🍀 자료구조 예시 2

int SBS = 6;
int KBS2 = 7;
int KBS1 = 9;
int EBS1 = 10;
int MBC = 11;
  • 방송국 이름과 채널 번호를 나타내는 변수를 프로그래머가 연관시켜야 한다.
  • 방송국 중 한 개를 이름과 채널 번호로 출력한다.
    Console.WriteLine("television networks: {0}, channel number: {1}", "SBS", SBS);

표를 프로그램에 저장해놓고, 방송국의 이름을 이용해 채널 번호를 찾울 수 있도록 구현한다.

방송국 이름을 인덱스처럼 사용할 수 있는 배열 channels[]

channels["SBS"] = 6;
Console.WriteLine("television networks: {0}, channel number: {1}", "SBS", channels["SBS"]);

자료구조를 이용할 때의 장점

  • 자료가 서로 연관되어 있을 때, 동일한 변수 이름을 사용할 수 있다.
    ⇒ 변수를 여러 개 사용할 때보다 가독성이 높고 코드 이해의 용이성
  • 자료 항목 각각에 연산 또는 비슷한 연산을 처리하는 경우, 반복문을 사용해 코드를 줄이기 쉽다.

📍 컬렉션

  • 객체를 쉽게 다룰 수 있도록 여러가지 클래스와 인터페이스를 미리 정의한 자료 구조
  • ArrayList SortedList Hashtable Stack Queue NameValueCollection

컬렉션 특징

  • 데이터를 보관할 수 있고, 수정, 삭제, 삽입, 검색 등의 기능을 갖는다.
  • 클래스마다 구현되어지는 알고리즘이 다를 뿐, 같은 부류다.
  • 동적으로 메모리 확장이 가능하다.

다양한 컬렉션

  • .NET은 다양한 컬렉션을 제공한다.
  1. System.Collections 클래스
  2. System.Collections.Generic 클래스
  3. System.Collections.Concurrent 클래스

Object 객체 컬렉션 클래스

  1. ArrayList : 필요에 따라 크기가 동적으로 증가하는 개체 배열
  2. Hashtable : 키의 해시코드에 따라 구성된 키값 쌍의 컬렉션
  3. Queue : FIFO 방식의 개체 컬렉션 (선입선출)
  4. Stack : LIFO 방식의 개체 컬렉션 (선입후출)
  5. SortedList : 키에 따라 정렬된 키/값 쌍의 컬렉션

재네릭 컬렉션 클래스

  1. Dictionary : 키에 따라 구성도니 키/값 쌍의 컬렉션
  2. HashSet : 값 집합
  3. Linkedlist<T> : 이중 연결 목록
  4. List<T> : 순서가 있어서 인덱스로 액세스할 수 있는 개체 목록
  5. Queue<T> : 선입선출 (FIFO)
  6. SortedList<TKey,TValue> : 연관된 IComparer<T> 구현을 기반으로 키에 따라 정렬된 키ㅣ/값 쌍의 컬렉션
  7. Stack<T> : 선입후출(LIFO)



📍 SortedList

SortedList : Hashtable + ArrayList

  • 내부의 데이터는 키와 값으로 이루어져 있으며, 키를 기준으로 정렬되고 키와 인덱스로 접근할 수 있다.
  • 내부적으로 정렬된 컬렉션을 유지하고 있는 특징을 갖는다.
    pubilc class SoretdList: IDictionary, ICollection, IEnumerable, IConeable

SortedList 특징

  • 키의 목록 또는 값의 목록만 반환하는 메서드를 제공한다.
  • 내부적으로 두 개의 배열, 즉 키에 대한 배열과 값에 대한 배열을 유지하여 요소를 목록에 저장한다.
  • 각 요소에 대해 키, 값 또는 인덱스의 세가지 방법으로 접근할 수 있다.
  • 요소가 삽입되면, 지정된 키가 이미 존재하는 검사한다. (중복키 허용)

SortedList 메서드

  • Add() : 키와 값으로 데이터를 삽입한다.
  • Clear() : 모든 요소를 제거한다.
  • Contains() : 특정 키가 들어 있는지 여부를 확인한다.
  • ContainsKey() : 특정 키가 들어 있는지 여부를 확인한다.
  • GetByIndex() GetKey() : 지정한 인덱스에서 값/키를 가져온다.
  • GetKeyList() : 키 리스트를 가져온다.
  • Remove() RemoveAt() : 지정한 키/인덱스로 요소를 제거한다.
  • GetEnumerator() : IDictionaryEnumerator를 반환한다.



📍 Queue

Queue - FIFO 컬렉션

  • 선입선출 구조

Queue 메서드

  • Enqueue() : 큐의 첫 위치에 요소를 삽입한다.
  • Dequeue() : 큐의 마지막 위치의 요소를 반환하고 삭제한다.
  • Peek() : 마지막 위치의 요소를 제거하지 않고 반환한다. (반환되는 데이터형은 object형)

🍀 Queue 예제

Queue queue = new Queue(new object[] {10, 20, 30});
queue.Enqueue(1);
queue.Enqueue("abc");
queue.Enqueue(3.4);

foreach(object obj in queue)
	Console.WriteLine(obj);

while(queue.Count > 0)
{
	Console.WriteLine("Dequeue: {0} Count: {1}",
    	queue.Dequeue(), queue.Count);
}

출력 결과 : 10 20 30 1 abc 3.4


📍 Stack

Stack - LIFO 컬렉션

  • 선입후출 구조

Stack 메서드

  • Push() : 스택의 맨 위에 요소를 삽입한다.
  • Pop() : 스택의 맨 위에 있는 요소를 삭제하고 데이터를 반환한다. (반환 타입 : object)
  • Peek() : 스택의 맨 위에 있는 요소를 제거하지 않고 반환한다. (반환 타입 : object)

🍀 Stack 예제

Stack stack = new Stack(new object[] {10, 20, 30});
stack.Push(1);
stack.Push("abc");
stack.Push(3.4);

foreach(object obj in stack)
	Console.WriteLine(obj);

while(stack.Count > 0)
{
	Console.WriteLine("Pop: {0} Count: {1}", 
    	stack.Pop(), stack.Count);
}

출력 결과 : 3.4 abc 1 30 20 10

📍 ArrayList

ArrayList는 ILIST를 구현한 대표적인 클래스

  • 데이터를 삽입했을 때 순서대로 삽입되며, 중간 삽입이나 제거가 가능하다.

ArrayList 메서드

  • Add(), AddRange() : 데이터/데이터 리스트를 삽입한다.
  • Insert() : 중간에 데이터를 삽입한다.
  • Remove(), RemoveAt(), RemoveRange() : 해당 요소를 제거 or 인덱스로 요소 제거 or 범위만큼 요소 제거
  • Sort() : 요소 정렬
  • GetEnumerator() : IEnumerator를 반환한다.

🍀 ArrayList 예제

ArrayList list = new ArrayList();
list.Add(10);
list.Add(20);
list.Add(30);

foreach(object obj in list)
	Console.WriteLine(obj);

list.RemoveAt(1); // 1번 요소 제거

foreach(object obj in list)
	Console.WriteLine(obj);
    
list.Insert(1, 25); // 1번 요소에 25 추가

foreach(object obj in list)
	Console.WriteLine(obj);

⇒ 출력 결과 : 10 20 30 | 10 30 | 10 25 30



📍 Hashtable

Hashtable은 IDictionary를 구현한 클래스

  • 내부의 데이터는 키(Key)와 값(Value)을 이용한다.

Hashtable 메서드

  • Add() : (키, 변수)로 된 데이터를 삽입한다.
  • Clear() : 모든 요소를 제거한다.
  • Remove() : 키를 확인하여 요소를 삭제한다.
  • ContainsKey() ContainsValue() : 특정 키/값을 포함하는지 확인
  • CoptyTo() : 해쉬테이블에 있는 원소를 1차원 배열로 복사한다.
  • Keys, Values : ICollection으로 반환한다.
  • GetEnumerator() : IDictionaryEnumerator를 반환한다.

🍀 Hashtable 예제

Hashtable ht = new Hashtable();
ht.Add(1, "Dooly");
ht.Add(3, "Heedong");
ht.Add(2, "Gildong");
ht[4] = "Tochi";

if(!ht.ContainsKey(5))
	ht.Add(5, "Douner");
    
foreach(DictionaryEntry de in ht)
{
	Console.WriteLine("Key={0} Value={1}", 
    		de.Key, de.Value.ToString());
}

Console.WriteLine("After Remove Dooley);

ht.Remove(1); // 둘리 제거
foreach(DictionaryEntry de in ht)
{
	Console.WriteLine("Key={0} Value={1}",
    		de.Key, de.Value.ToString());
}

⇒ 출력 결과 : 5, 4, 3, 2, 1 | 5, 4, 3, 2
정렬된 컬렉션이 아니기 때문에 넣은 순서대로 출력되지 않는다.
해시값 순서로 순회되기 때문에 넣은 순서와 출력순서가 다를 수 있다.



📍 IEnumerable 인터페이스

  • GetEnumerator() : IEnumerator 개체를 반환한다.
  • 내부에서 IEnumerable을 사용하여 데이터 검색 기능을 제공한다.
  • Current 속성 : 컬렉션에서 현재 객체에 대한 참조를 반환한다.
  • MoveNext() : 다음 요소로 이동한다.
  • Reset() : Current 포인터를 컬렉션의 처음 앞으로 설정한다.



📍 ICollection 인터페이스

  • Count 속성 : 컬렉션의 객체 수를 반환한다.
  • IsSynchronized 속성 : 다중 스레드된 액세스를 위해 컬렉션에 대한 액세스를 동기화한 경우 true를 반환한다.
  • SyncRoot 속성 : 하나 이상의 코드 문장이 동시에 한 스레드에만 실행되는 것을 확실하게 하기 위해 잠그거나 해제한다.
  • CoptyTo() : 지정한 배열 위치부터 컬렉션 요소를 배열로 복사한다.



📍 IList 인터페이스

  • ICollection 인터페이스에서 파생된 것으로 IEnumerable과 ICollection 기능을 모두 포함한다.
  • IsFixedSize 속성 : 리스트가 고정 길이 리스트인지 확인
  • IsReadOnly 속성 : 리스트가 읽기 전용인지 확인
  • Add() : 리스트 끝에 데이터를 추가한다.
  • Clear() : 리스트 내의 모든 데이터를 제거한다.
  • Contains() : 어떤 데이터가 리스트 내에 존재하는지 여부를 확인한다.
  • IndexOf() : 리스트 내의 특정 데이터의 위치를 반환한다.
  • Insert() : 리스트 내의 특정 위치에 데이터를 삽입한다.
  • Remove() : 매개변수로 입력된 객체를 리스트 내에서 제거한다.
  • RemoveAt() : 지정한 인덱스의 데이터를 제거한다.



📍 IDictionary 인터페이스

  • 순서에 의존하는 IList와 달리 키와 값으로 대응시켜 데이터를 추출한다.

  • IsFixedSize 속성 : 컬렉션의 크기가 정해져 있는지 검사한다.

  • IsReadOnly 속성 : 컬렉션이 읽기 전용인지 확인한다.

  • Keys 속성 : 컬렉션 내의 모든 키를 나열한다.

  • Values 속성 : 컬렉션 내의 모든 값을 나열한다.

  • Add() : 키와 값을 전달하여 데이터를 컬렉션에 추가한다.

  • Clear() : 컬렉션의 모든 데이터를 제거한다.

  • Contains() : 특정 키가 데이터와 연관되어 있는지 검사한다.

  • GetEnumerator() : IDictionaryEnumberator를 반환한다.

  • Remove() : 삭제할 값의 키를 전달하여 데이터를 컬렉션에서 제거한다.



📍 IDictionaryEnumerator 인터페이스

  • DictionaryEntry 속성 : 열거 요소 내의 키와 값을 가져온다.
  • Key 속성 : 열거 요소 내의 키를 가져온다.
  • Value 속성 : 열거 요소 내의 값을 가져온다.



📍 컬렉션 클래스의 데이터 입출력

  • 컬렉션 클래스에 데이터 입출력이 있으면 그때마다 자동으로 boxing과 unboxing이 계속 발생한다.
  • 컬렉션 클래스는 전부 object 타입으로 저장하기 때문에 데ㅣ터에 접근할 때마다 본래 타이븡로의 형식 변화가 일어나기 때문이다.
  • 데이터가 많아질수록 컴퓨터 성능에 상당한 부하가 발생한다.
  • 성능 상의 이슈가 문제가 된다면, Collections 클래스보다 Generic Collections를 사용해야 한다.

0개의 댓글