Quick Reference
HashSet<T>는 중복을 허용하지 않고 포함 여부를 자주 확인할 때 씁니다. Add는 실제로 추가했을 때만 true를 반환하고, Contains의 평균 비용은 O(1)입니다. 인덱스 접근과 순서 보장은 없습니다.
var visited = new HashSet<int>();
if (visited.Add(42))
{
// 42를 처음 방문했을 때만 실행
}
bool seen = visited.Contains(42);
bool removed = visited.Remove(42);- 중복 제거와 빠른 membership 검사는
HashSet<T>가 맞습니다. - 입력 순서나 정렬 순서가 결과에 필요하면
List<T>또는SortedSet<T>를 검토합니다. - 문자열을 대소문자 없이 비교하려면
new HashSet<string>(StringComparer.OrdinalIgnoreCase)로 만듭니다.
원소 추가와 관계 검사
Add의 반환값은 “이미 있었는가”를 검사하는 데 바로 쓸 수 있습니다. 빈 집합에서 꺼내는 API가 없으므로, 집합은 순서가 아닌 membership을 모델링할 때 적합합니다.
var tags = new HashSet<string> { "csharp", "dotnet" };
bool firstAsyncTag = tags.Add("async"); // true
bool duplicate = tags.Add("csharp"); // false
tags.Remove("dotnet");
tags.RemoveWhere(tag => tag.StartsWith("temp-"));
bool overlaps = tags.Overlaps(["async", "unity"]);
bool containsAll = tags.IsSupersetOf(["csharp", "async"]);
bool sameMembers = tags.SetEquals(["async", "csharp"]);SetEquals는 순서를 보지 않고 원소 구성이 같은지만 확인합니다. IsSubsetOf, IsProperSubsetOf, IsSupersetOf, Overlaps는 원본을 바꾸지 않습니다. 반환값만 필요한 판단에는 이 메서드를 쓰고, 결과 집합 자체가 필요할 때만 다음의 변경 메서드를 호출합니다.
합집합과 교집합
UnionWith, IntersectWith, ExceptWith, SymmetricExceptWith는 호출한 집합을 직접 수정합니다. 원본을 보존하려면 복사본에서 연산합니다.
var backend = new HashSet<string> { "api", "worker", "web" };
var enabled = new[] { "api", "web", "admin" };
var activeBackend = new HashSet<string>(backend);
activeBackend.IntersectWith(enabled); // api, web
var all = new HashSet<string>(backend);
all.UnionWith(enabled); // api, worker, web, admin
var backendOnly = new HashSet<string>(backend);
backendOnly.ExceptWith(enabled); // workerLINQ의 Distinct, Union, Intersect, Except는 새 IEnumerable<T> 흐름을 만들고, HashSet<T>의 ...With 메서드는 기존 인스턴스를 바꾼다는 차이가 있습니다. 공유된 HashSet<T>에 IntersectWith를 호출하면 다른 코드도 줄어든 원소를 보게 됩니다.
비교자와 선택 기준
HashSet<T>는 IEqualityComparer<T>로 중복을 판정합니다. 같다고 판단되는 두 원소는 같은 해시 코드를 반환해야 하며, 집합에 넣은 뒤 비교 기준 필드를 바꾸면 안 됩니다.
var usernames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Mina",
};
Console.WriteLine(usernames.Add("mina")); // FalseList<T>는 순서와 인덱스가 중요할 때, HashSet<T>는 중복 없는 포함 검사가 중요할 때, SortedSet<T>는 정렬된 순서로 중복 없는 원소를 순회해야 할 때 맞습니다. HashSet<T>의 순회 순서는 계약이 아니므로 화면이나 직렬화 결과의 순서를 여기에 맡기지 않습니다.
자주 틀리는 부분
Add후Count를 비교하지 말고Add의bool반환값을 사용합니다.UnionWith와IntersectWith는 결과를 반환하지 않고 원본을 수정합니다.HashSet<T>를 순회하는 동안 원소를 추가하거나 제거하면 안 됩니다. 필터링은RemoveWhere또는 복사본으로 처리합니다.- 변경 가능한 객체를 키로 쓰면
Contains와Remove가 예상대로 동작하지 않을 수 있습니다. 비교 기준을 불변으로 둡니다.
참고 링크
2 sources