아디봉의.net
C# 컬렉션(Collection) 다루기 -발표자료 본문
제네릭(클래스나 메서드에 사용할 타입을 파라미터화 할수 있게 해준다는것 )
일반메서드 안에서 사용할 값을 넘기는 것과 비슷하게 제네릭 타입과 메서드에서 사용할 타입을 지정할 수 있도록 파라미터들이 있다. 제네릭이 필요한 이유라면 C#1에서 소스코드에서 캐스팅을 많이 최소화 하기위함 이라고 할수 있겠다. 보통 캐스팅은 변수나 메서드 선언하는 시점에 하는데 제네릭을 사용하면 사용자 올바르지 않은 캐스팅을 막을수 있다.
또 제네릭에서 제공하는 새로운 정보를 이용하면 생산성을 높일수 있다. 더많은 검사가 컴파일시에 일어난다.
일반적인 타입사용하는장소에 타입파라미터(실제타입을 대입할수있는장소)를 대신사용한다. 타입파라미터는 제네릭 선언부의 꺽쇠(< >)괄호 사이에 넣어주고 여러개의 파라미터는 , 콤마를 이용해 구분지음
따라서 Dictionary <Tkey, Tvalue>에서 타입파라미터는 Tkey와 Tvalue 다.
파라미터인자 미지정 - 바인드되지않은 제네릭타입( unbound generic type)
파라미터인자 지정 - 구성된 타입(constructed type)
구성된 타입은 열려(open)있거나닫혀(closed)있을수도 있다.
열린타입(Dictionary<Tkey,Tvalue>)은 아직결정되지 않은 타입파라미터가 존재
안열린타입(Dictionary<string,int>)은 모든파라미터가 정해진 경우를 말함
**참고 제네릭 파라미터선언관련
타입파라미터 이름에 T,U,V등을 사용할수도 있으나 이렇게 하면 파라미터 의미를 알아보기 어렵다. Dictionary<Tkey. Tvalue> 를 살펴보면 Tkey 와 Tvalue 는 각각 key 값으로 사용되는 타입과 value 값으로 사용되는 타입을 나타낸다는 것이 명확하다.
파라미터가 한개뿐일때는 보통 그 의미가 명확하고 일반적으로 T를 사용한다. (ex<List T>)
정리해보면 2개이상시에는 파라미터의미가 필요한 경우에는 해당 파라미터의 의미에 따라 이름을 지어주는 것이 좋으며 타입파라미터라는 것을 알리기위해 T를 접두어로 사용하는 것이 좋다.
제네릭 타입과 타입 파라미터
1) 제네릭 타입 = 클래스, 인터페이스, 델리게이트, 구조체 (제네릭에 enum 은없다.)
2) 제네릭 메서드 = 제네릭타입의 사용될 타입이 무엇인지 명확한 경우에도 특정메서드는 자신만의 별도의 타입 파라미터를 가질수 있다는 것이다.
반환타입 메서드이름 타입파라미터 타입 converter= 파라미터이름
(제네릭리스트) <타입파라미터>
ex) List<TOutPut> ConverAll<TOutPut> (Converter<T,TPutPut> converter)
{}
List<Guid>ConverAll<Guid> (Converter<String ,Guid> converter
{}
메소드 설명
1) 이 메서드는 List<Guid>를반환한다.
2) 이 메서드의 이름은 ConverAll 이다
3) 이 메서드는 하나의 타입 파라미터를 가지며 이번 예제에서 사용한 타입 인자는 Guid이다.
4) 이메서드는 하나의 파라미터를 받으며 이는 Converter<String, Guid> 타입이고 이름은 converter이다.
* Converter<String, Guid> 란?
Converter<String, Guid>는 구성된 제네릭 타입이며 string 을 GUID로 변환하는데 사용
즉 string목록(list)을 변환하여 GUID목록을 반환하는 메서드를 의미
using System; namespace ConsoleApplication3 Converter<int,double> converter = TakeSquareRoot; List<double> doubles; foreach(double d in doubles) |
제네릭 타입의 메서드 시그니처
public void Add(TKey key, TValue value)
public TValue this [TKey Key]{get; set;}
public bool ContainsValue(TValue value)
public bool ContainsKey(TKey key)
타입 파라미터를 대치한 후의 메서드 파라미터
public void Add(String key, int value)
public int this[string key]{get: set:}
pubic bool ContainsValue(int value)
public vool Containskey (String key)
* 제네릭 타입의 메서드 시그니처들은 타입에 정보를 담을 장소만을 제공하고 이는 타입인자를 지정하면서 대치된다.
Collection
컬렉션은 일종의 배열이라고 생각하면 쉽다.
다른점은 컬렉션(크기가 동적변경), 배열은 (고정된 메모리영역에서 동일한 데이터를 다룸) 닷넷은 특정 알고리즘에 최적화된 다양한 컬렉션 클래스를 제공 (LinkedList, Stack, Queue , Hash등) 컬렉션은 데이터 삽입, 검색, 삭제, 갱신 등의 작업을 쉽게 할수 있도록 제공해줌
리스트
List<T>
List<T>는 대부분의 경우 기본적으로 사용되는 리스트이다.
이타입은 IList<T> 인터페이스를 구현하므로 ,ICollection<T> , IEnumerable<T>, IEnumerable 도 구현하고 있다.
비제네릭 타입으로는 ICollection, IList 인터페이스도 구현하고 있다.
내부적으로 List<T>는 배열을 저장하며 리스트의 논리적 크기와 내부배열(backing array) 의 실제크기를 추적한다.
배열에 요소추가시 배열에 최대인덱스 다음위치에 값을 지정하거나, 가득찬상태라면 더큰배열을 생성하여 새배열에 복사한 다음 새로운 값을 지정
List<T> 메서드
- convertAll은 특정 리스트를 다른 리스트로 프로젝트 연산을 한다.
- FindAll필터는 새로운 리스트를 생성하고 원래의 리스트에서 특정 조건에 맞는 요소들을 골라내어 새로운 리스트에 담는다.
- sort 는 해당 타입의 기본 등가비교자 혹은 인수로 지정된 비교자를 사용하여 정렬을 수행한 다. (sort와 linq의 order by 사이에 큰차이는 없다. sort는 원래의 리스트를 정렬 , order by는 정렬된 복사본을 새로 생성 또한 sort는 불안정하며 order by 는 안정적이다.)
- ForEach 메서드는 이름그대로 리스트에서 값을 꺼내어 각 값에 대해 메서드의 인수로 지정된 대리자를 반복하여 실행한다.
LinkedList<T>
LinkedList<T>는 여러모로 봤을 때 리스트이지만 (항목을 추가할 때 항목의 순서를 유지하는 컬렉션이라는 면에서), IList<T>인터페이스를 구현하지 않고 있다.
LinkedList<T>는 고전컴퓨터과학에서 말하는 이중연결리스트 이다.
이중연결리스트는 헤드(head)와 테일(tail)노드를 관리하며, 각각의 노드는 리스트 내의 이전 노드와 다음노드에 대한 참조를 갖고 있다.
각각의 노드는 LinkedListNote<T>타입으로 외부에 노출되며 , 리스트 중간 어느위치에서든 편리하게 삽입/삭제 작업을 수행할 수있다.
연결리스트는 배열기반의 리스트와 비교해 공간을 효율적으로 사용하지 않으며, 인덱스 연산을 지원하지 않음, 하지만 적절한 위치의 노드에 대한 참조를 갖고 있을 경우, 리스트 내 임의 위치에 요소를 추가/ 삭제할때는 훨씬빠르다.
LinkedList<T> 메서드
- Add(리스트의 끝에 요소를 추가한다)와 같은 표준 메서드를 구현하고 있지만, 명확히 어떤 작업을 하는지를 나타내도록 AddFirst, AddLast 같은 메서드를 사용하는 게 좋다.
- RemoveFirst, RemoveLast 메서드
- First, Last 속성도 존재한다. 이들 메서드와 속성은 리스트 내 노드의 값이 아니라 노드자체를 반환한다. 만약 리스트가 비어 있을 경우, First,Last 속성은 Null을 반환한다.
Collection<T>
Collection<T> 은 System.Collections.ObjectModels 네임스페이스의 멤버이다.
List<T> 와 마찬가지로 제네릭, 비제네릭 컬렉션 인터페이스를 구현하고있다.
Collection<T>을 직접사용하는 것도 가능하지만 이 컬렉션은 보통 기본 클래스로서 더많이 사용된다. collection<T> 타입은 다른 리스트의 래퍼처럼 동작한다. 생성자에서 래핑할 리스트를 지정할 수 있으며, 그렇지 않은 경우에는 내부적으로 새로운 List<T>객체가 생성된다.
사전
Dictionary<Tkey, Tvalue>
특별한 요구사항이 없다면 Dictionary(Tkey, Tvalue> 이 기본적으로 사용하게 될 사전일 것이다. 이 사전은 기본 리스트 구현인 List<T>와 비슷한 방식으로 사용할 수 있다.
Dictionary<Tkey, Tvalue>효율적인 검색을 구현하기 위해 해시테이블 을 사용하고 있다.
Dictionary<Tkey, Tvalue>는 혼란을 야기할수 있는 중요한 요소가 있는데, Dictionary<Tkey, Tvalue>내에서의 항목 순서가 보장되지 않는다는 것이다. 만약 사전에 항목을 추가한 다음에 사전을 반복해 처리한다면, 해당항목이 애초에 입력한 순서대로 나올것이라고 예상할수 있다. 하지만 이는 어떻게 될지 모르는 일이다. 구현에 따라서 입력한 항목의 순서를 유지하는 사전이 있는가 하면, 순서에 뒤섞는 사전 또한 존재한다.
Dictionary<Tkey, Tvalue>는 List<T>처럼 배열내에 자신의 항목을 유지하면서 필요할 때 배열을 확장한다.
SortedList<Tkey, Tvalue> & SortedDictionary<Tkey, Tvalue>
SortedList<, > 라는 이름의 클래스를 보면 아마 리스트가 아닐까하고 생각되겠지만 그렇지 않고, 실제로는 사전타입이며 IList<T>를 구현하지도 않는다.
이들 두 클래스는 여러 공통점이 있다. 키의 값을 비교하기 위해 IEqualityComparer<Tkey>대신 IComparer<Tkey>를 사용하며, 이들 비교 인터페이스를 사용하여 정렬된 키목록을 관리한다.
하지만 내부데이터 구조는 서로 다른데 , SortedList<,>는 정렬을 유지하는 배열을 사용하고 SortedDictionary<,>은 레드-블랙 트리구조를 사용한다.
이때문에 두 타입은 삽입/제거 작업에서 시간과 메모리 효율성에 있어 현저한 차이를 보인다. 정렬된 데이터로부터 사전을 생성할 경우에는 SortedList<,>가 효율적이고 , List<T>를 정렬된 상태로 유지한다고 했을때 리스트의 끝에 항목 하나를 추가하는 것은 비용이 얼마들지 않지만 항목을 임의의 위치에 추가하는 것은 비용이 많이 발생한다.
SortedList<,>의 균형트리에 항목을 추가하는 것은 비교적 적은 비용이 들지만 , 각항목에 해당하는 힙에 트리노드를 생성해야 하므로 더 많은 오버헤드와 파편화가 이뤄진다.
집합
Queue<T>, Stack<T>
Queue - FIFO(first in first out, 선입선출) , stack - LIFO(last in first out, 후입선출) 구조라고 불리곤하다. 두 데이터구조의 기본아이디어는 동일하며 컬렉션 항목을 추가하고, 컬렉션 항목을 제거한다.
두데이터 구조의 차이점은 어느 쪽에서 항목을 추가하고 제거 하느냐이다.
Queue는 가계앞에서 쭉 늘어선 줄처럼 처리된다. 줄을 함류한 첫번째 사람이 먼저 서비스를 받는다.
stack 는 접시더미와 비슷하다. 마지막으로 맨위에 쌓인 접시를 가장먼저 가져가게 된다. 큐와 스택의 일상적인 사용법중 하나는 진행하려는 작업 항목의 목록 유지하기 위해서 이다.
Queue<T>는 환형(circular) 버퍼로 구현된다. 기본적으로 Queue<T>는 항목이 추가될 다음 슬롯을 기억하는 인덱스와 다음에 저거할 항목이 들어있는 슬롯을 기억하는 인덱스가 존재하는 배열을 관리한다. 만약 추가인덱스가 제거인덱스의 값을 초과하면, 내부 항목들은 좀더 큰 배열로 복사된다. Queue<T> 는 항목을 추가/제거하는 Enqueue, Dequeue메서드를 제공 Peek 메서드는 다음에 큐에서 제거될 항목을 실제로 제거하지 않고도 알수 있게 해준다. 큐를 반복처리할 경우 항목들의 큐에서 순서대로 제거된다. Stack<T> Stack<T> 의 구현은 Queue<T>보다 훨씬 간단하다. 리스트의 끝에서만 항목을 추가(push)하거나 제거(pop)할수있다. peek 메서드는 항목을 제거하지 않고 다음에 제거될 항목을 확인할수 있다. 스택을 반복처리할 경우, 스택에 가장 마지막에 추가된항목이 먼저 제거된다.
Queue<T>