다음을 통해 공유


컬렉션 및 데이터 구조

컬렉션으로 저장하고 조작할 때 유사한 데이터를 보다 효율적으로 처리할 수 있는 경우가 많습니다. 클래스 또는 System.Array 클래스와 System.Collections, System.Collections.Generic, System.Collections.Concurrent, System.Collections.Immutable 네임스페이스의 클래스를 사용하여 컬렉션에서 개별 요소 또는 요소 범위를 추가, 제거 및 수정할 수 있습니다.

컬렉션에는 두 가지 주요 형식이 있습니다. 제네릭 컬렉션 및 제네릭이 아닌 컬렉션입니다. 제네릭 컬렉션은 컴파일 시 형식이 안전합니다. 따라서 제네릭 컬렉션은 일반적으로 더 나은 성능을 제공합니다. 제네릭 컬렉션은 생성될 때 형식 매개 변수를 허용합니다. 컬렉션에서 항목을 추가하거나 제거할 때 형식에서 Object 캐스팅할 필요가 없습니다. 또한 대부분의 일반 컬렉션은 Windows 스토어 앱에서 지원됩니다. 제네릭이 아닌 컬렉션은 항목을 저장할 때 Object 캐스팅이 필요하며, 이로 인해 대부분이 Windows 스토어 앱 개발에 지원되지 않습니다. 그러나 이전 코드에는 제네릭이 아닌 컬렉션이 표시될 수 있습니다.

.NET Framework 4 이상 버전에서 네임스페이스의 System.Collections.Concurrent 컬렉션은 여러 스레드에서 컬렉션 항목에 액세스하기 위한 효율적인 스레드로부터 안전한 작업을 제공합니다. 네임스페이스(System.Collections.Immutable)의 변경할 수 없는 컬렉션 클래스는 원래 컬렉션의 복사본에서 작업을 수행하고 원래 컬렉션을 수정할 수 없으므로 기본적으로 스레드로부터 안전합니다.

일반적인 컬렉션 기능

모든 컬렉션은 컬렉션에서 항목을 추가, 제거 또는 찾기 위한 메서드를 제공합니다. 또한 ICollection 인터페이스 또는 ICollection<T> 인터페이스를 직접 또는 간접적으로 구현하는 모든 컬렉션은 이러한 기능을 공유합니다.

  • 컬렉션을 열거하는 기능

    .NET 컬렉션은 컬렉션을 반복할 수 있도록 System.Collections.IEnumerable 또는 System.Collections.Generic.IEnumerable<T>을 구현합니다. 열거자는 컬렉션의 모든 요소에 대한 이동 가능한 포인터로 생각할 수 있습니다. foreach, in statement 및 For Each… Next 문GetEnumerator 메서드에 의해 노출되는 열거자를 사용하고, 열거자를 조작하는 복잡성을 숨깁니다. 또한 구현 System.Collections.Generic.IEnumerable<T> 하는 모든 컬렉션은 쿼리 가능한 형식 으로 간주되며 LINQ를 사용하여 쿼리할 수 있습니다. LINQ 쿼리는 데이터에 액세스하기 위한 일반적인 패턴을 제공합니다. 일반적으로 표준 foreach 루프보다 간결하고 읽기 가능하며 필터링, 순서 지정 및 그룹화 기능을 제공합니다. LINQ 쿼리는 성능을 향상시킬 수도 있습니다. 자세한 내용은 LINQ to Objects(C#), LINQ to Objects(Visual Basic), PLINQ(병렬 LINQ),LINQ 쿼리 소개(C#)기본 쿼리 작업(Visual Basic)을 참조하세요.

  • 컬렉션 콘텐츠를 배열에 복사하는 기능

    모든 컬렉션은 메서드를 사용하여 CopyTo 배열에 복사할 수 있습니다. 그러나 새 배열의 요소 순서는 열거자가 반환하는 시퀀스를 기반으로 합니다. 결과 배열은 항상 하한이 0인 1차원입니다.

또한 많은 컬렉션 클래스에는 다음과 같은 기능이 포함되어 있습니다.

  • 용량 및 개수 속성

    컬렉션의 용량은 포함할 수 있는 요소의 수입니다. 컬렉션의 수는 실제로 포함된 요소의 수입니다. 일부 컬렉션은 용량 또는 개수 또는 둘 다를 숨깁니다.

    현재 용량에 도달하면 대부분의 컬렉션이 자동으로 용량으로 확장됩니다. 메모리가 다시 할당되고 요소가 이전 컬렉션에서 새 컬렉션으로 복사됩니다. 이 디자인은 컬렉션을 사용하는 데 필요한 코드를 줄입니다. 그러나 컬렉션의 성능에 부정적인 영향을 미칠 수 있습니다. 예를 들어 List<T>에서, CountCapacity보다 작으면 항목을 추가하는 것은 O(1) 작업입니다. 새 요소를 수용하기 위해 용량을 늘려야 하는 경우 항목을 추가하면 O(n) 작업이 nCount됩니다. 여러 재할당으로 인한 성능 저하를 방지하는 가장 좋은 방법은 초기 용량을 컬렉션의 예상 크기로 설정하는 것입니다.

    A BitArray 는 특별한 경우입니다. 용량은 길이와 같으며 개수와 동일합니다.

  • 일관된 하한

    컬렉션의 하한은 첫 번째 요소의 인덱스입니다. 네임스페이스의 System.Collections 모든 인덱싱된 컬렉션은 하한이 0이므로 0으로 인덱싱됩니다. Array에는 기본적으로 하한이 0이지만 , 을 사용하여 Array.CreateInstance 클래스의 인스턴스를 만들 때 다른 하한을 정의할 수 있습니다.

  • 여러 스레드에서 액세스하기 위한 동기화 (System.Collections 클래스에만 해당).

    네임스페이스 System.Collections의 제네릭이 아닌 컬렉션 형식은 동기화를 통해 일부 스레드 보안을 제공하며, 일반적으로 SyncRootIsSynchronized 멤버를 통해 노출됩니다. 이러한 컬렉션은 기본적으로 스레드로부터 안전하지 않습니다. 컬렉션에 확장 가능하고 효율적인 다중 스레드 액세스가 필요한 경우 네임스페이스의 System.Collections.Concurrent 클래스 중 하나를 사용하거나 변경할 수 없는 컬렉션을 사용하는 것이 좋습니다. 자세한 내용은 Thread-Safe 컬렉션을 참조하세요.

컬렉션 선택

일반적으로 제네릭 컬렉션을 사용해야 합니다. 다음 표에서는 이러한 시나리오에 사용할 수 있는 몇 가지 일반적인 컬렉션 시나리오 및 컬렉션 클래스에 대해 설명합니다. 제네릭 컬렉션을 처음으로 사용하는 경우 다음 표는 작업에 가장 적합한 제네릭 컬렉션을 선택하는 데 도움이 됩니다.

하고 싶어요... 제네릭 컬렉션 옵션 제네릭이 아닌 컬렉션 옵션 스레드에 안전하거나 불변하는 컬렉션 옵션
키별 빠른 조회를 위해 항목을 키/값 쌍으로 저장 Dictionary<TKey,TValue> Hashtable

(키의 해시 코드를 기반으로 구성된 키/값 쌍의 컬렉션입니다.)
ConcurrentDictionary<TKey,TValue>

ReadOnlyDictionary<TKey,TValue>

ImmutableDictionary<TKey,TValue>
인덱스별 항목 액세스 List<T> Array

ArrayList
ImmutableList<T>

ImmutableArray
아이템을 선입선출 방식으로 사용하세요 Queue<T> Queue ConcurrentQueue<T>

ImmutableQueue<T>
후입선출(LIFO) 방식으로First-Out 데이터를 사용하기 Stack<T> Stack ConcurrentStack<T>

ImmutableStack<T>
순차적으로 항목 액세스 LinkedList<T> 권장 사항 없음 권장 사항 없음
항목이 제거되거나 컬렉션에 추가될 때 알림을 받습니다. (구현 INotifyPropertyChanged 그리고 INotifyCollectionChanged) ObservableCollection<T> 권장 사항 없음 권장 사항 없음
정렬된 컬렉션 SortedList<TKey,TValue> SortedList ImmutableSortedDictionary<TKey,TValue>

ImmutableSortedSet<T>
수학 함수 집합 HashSet<T>

SortedSet<T>
권장 사항 없음 ImmutableHashSet<T>

ImmutableSortedSet<T>

컬렉션의 알고리즘 복잡성

컬렉션 클래스를 선택할 때 성능의 잠재적인 절충을 고려할 가치가 있습니다. 다음 표를 사용하여 알고리즘 복잡성에서 다양한 변경 가능한 컬렉션 형식이 해당 변경할 수 없는 컬렉션 형식과 비교하는 방법을 참조합니다. 변경할 수 없는 컬렉션 형식은 성능이 떨어지지만 불변성을 제공하는 경우가 많으며 이는 종종 유효한 비교 이점입니다.

변경 가능 상환 최악의 경우 변경 불가능 복잡성
Stack<T>.Push O(1) O(n) ImmutableStack<T>.Push O(1)
Queue<T>.Enqueue O(1) O(n) ImmutableQueue<T>.Enqueue O(1)
List<T>.Add O(1) O(n) ImmutableList<T>.Add O(log n)
List<T>.Item[Int32] O(1) O(1) ImmutableList<T>.Item[Int32] O(log n)
List<T>.Enumerator O(n) O(n) ImmutableList<T>.Enumerator O(n)
HashSet<T>.Add조회 O(1) O(n) ImmutableHashSet<T>.Add O(log n)
SortedSet<T>.Add O(log n) O(n) ImmutableSortedSet<T>.Add O(log n)
Dictionary<T>.Add O(1) O(n) ImmutableDictionary<T>.Add O(log n)
Dictionary<T> 조회 O(1) O(1) – 또는 엄밀하게 O(n) ImmutableDictionary<T> 조회 O(log n)
SortedDictionary<T>.Add O(log n) O(n log n) ImmutableSortedDictionary<T>.Add O(log n)

List<T>for 루프 또는 foreach 루프를 사용하여 효율적으로 열거할 수 있습니다. 그러나 인덱서의 O(로그 ImmutableList<T>) 시간으로 인해 for 루프 안에서는 효율적이지 않습니다. ImmutableList<T>foreach 루프를 사용하여 열거하는 것은 ImmutableList<T>가 데이터를 저장할 때 List<T> 배열 대신 이진 트리를 사용하기 때문에 효율적입니다. 배열을 빠르게 인덱싱할 수 있는 반면, 원하는 인덱스가 있는 노드가 발견될 때까지 이진 트리를 아래로 이동해야 합니다.

SortedSet<T> 또한 둘 다 이진 트리를 사용하기 때문에 복잡성이 ImmutableSortedSet<T> 동일합니다. 중요한 차이점은 변경할 수 없는 이진 트리를 사용한다는 ImmutableSortedSet<T> 것입니다. ImmutableSortedSet<T> 또한 변형을 허용하는 클래스를 System.Collections.Immutable.ImmutableSortedSet<T>.Builder 제공하므로 불변성과 성능을 모두 가질 수 있습니다.

제목 설명
컬렉션 클래스 선택 다양한 컬렉션을 설명하고 시나리오에 맞게 컬렉션을 선택하는 데 도움이 됩니다.
일반적으로 사용되는 컬렉션 형식 일반적으로 사용되는 제네릭 및 제네릭이 아닌 컬렉션 형식(예: System.Array, System.Collections.Generic.List<T>System.Collections.Generic.Dictionary<TKey,TValue>)에 대해 설명합니다.
제네릭 컬렉션을 사용하는 경우 제네릭 컬렉션 형식의 사용에 대해 설명합니다.
컬렉션 내의 비교 및 정렬 컬렉션에서 같음 비교 및 정렬 비교의 사용에 대해 설명합니다.
정렬된 컬렉션 형식 정렬된 컬렉션 성능 및 특성을 설명합니다.
해시 테이블 및 사전 컬렉션 형식 제네릭 및 제네릭이 아닌 해시 기반 사전 형식의 기능을 설명합니다.
Thread-Safe 모음집 여러 스레드에서 안전하고 효율적인 동시 액세스를 지원하는 컬렉션 System.Collections.Concurrent.BlockingCollection<T>System.Collections.Concurrent.ConcurrentBag<T> 형식에 대해 설명합니다.
System.Collections.Immutable 변경할 수 없는 컬렉션을 소개하고 컬렉션 형식에 대한 링크를 제공합니다.

참고 문헌