ImmutableHashSet
ImmutableHashSet<T>
는 .NET에서 제공하는 불변 컬렉션Immutable Collections 중 하나로, 변경 불가능한 해시셋을 관리합니다. 불변 해시셋은 생성된 이후에 상태가 변경되지 않으며, 데이터의 추가나 삭제가 필요할 때마다 새로운 해시셋을 생성합니다. 이러한 특성은 데이터 무결성을 보장하고, 멀티스레드 환경에서의 동시성 문제를 방지하는 데 큰 장점이 있습니다.
불변 해시셋의 특징
- 변경 불가능성: 해시셋이 생성된 이후에는 상태가 변경되지 않습니다. 데이터를 추가하거나 삭제할 때마다 새로운 해시셋이 생성됩니다.
- 구조적 공유: 새로운 해시셋을 생성할 때 전체 데이터를 복사하지 않고, 변경된 부분만 복사하여 메모리 사용을 최적화합니다.
- 스레드 안전성: 불변 해시셋은 멀티스레드 환경에서 안전하게 사용할 수 있어, 동기화에 대한 고민을 덜어줍니다.
기본 사용법
using System;
using System.Collections.Immutable;
class Program
{
static void Main()
{
ImmutableHashSet<int> hashSet = ImmutableHashSet<int>.Empty;
hashSet = hashSet.Add(1);
hashSet = hashSet.Add(2);
hashSet = hashSet.Add(3);
Console.WriteLine(string.Join(", ", hashSet));
// 출력: 1, 2, 3
hashSet = hashSet.Remove(2);
Console.WriteLine(string.Join(", ", hashSet));
// 출력: 1, 3
}
}
ImmutableHashSet<int>.Empty
를 사용하여 빈 해시셋을 생성합니다.Add
를 호출하여 요소를 추가하면 새로운 해시셋이 반환됩니다.Remove
를 호출하여 요소를 제거하면 새로운 해시셋이 반환됩니다.- 기존 해시셋은 변경되지 않고 그대로 유지됩니다.
주요 메서드와 활용
생성 및 초기화
// 빈 해시셋 생성
ImmutableHashSet<int> emptyHashSet = ImmutableHashSet<int>.Empty;
// 요소를 추가하여 해시셋 생성
ImmutableHashSet<int> hashSet = emptyHashSet.Add(1).Add(2).Add(3);
// 컬렉션 초기화
var initialHashSet = ImmutableHashSet.Create(1, 2, 3);
요소 추가 및 제거
- Add: 해시셋에 요소를 추가하여 새로운 해시셋을 반환합니다.
var newHashSet = hashSet.Add(4);
- AddRange: 여러 요소를 한꺼번에 추가합니다.
var newHashSet = hashSet.AddRange(new[] { 5, 6, 7 });
- TryAdd: 요소를 추가하고, 추가 성공 여부를 반환합니다. 요소가 이미 존재하는 경우 false를 반환합니다.
bool added = hashSet.TryAdd(4);
// 추가 성공 시 true, 이미 존재하는 경우 false 반환
- Remove: 특정 요소를 제거하여 새로운 해시셋을 반환합니다.
var updatedHashSet = hashSet.Remove(2);
- RemoveRange: 여러 요소를 제거합니다.
var updatedHashSet = hashSet.RemoveRange(new[] { 1, 3 });
요소 접근 및 수정
- Contains: 해시셋에 특정 요소가 포함되어 있는지 여부를 확인합니다.
bool contains = hashSet.Contains(2);
// contains는 true 또는 false입니다.
- GetValueOrDefault: 특정 요소를 찾고, 존재하지 않을 경우 기본값을 반환합니다.
int value = hashSet.GetValueOrDefault(2, -1);
// 요소가 존재하면 해당 값을, 존재하지 않으면 -1을 반환합니다.
bool contains = hashSet.Contains(2);
- Clear: 모든 요소를 제거하고 빈 해시셋을 반환합니다.
var clearedHashSet = hashSet.Clear();
- ToBuilder : 변경 가능한
ImmutableHashSet.Builder
로 변환하여 여러 번의 변경 작업을 효율적으로 수행한 후, 다시 불변 리스트로 변환합니다.
var builder = hashSet.ToBuilder();
builder.Add(4);
builder.Add(5);
var updatedHashSet = builder.ToImmutable();
// 기존 해시셋은 변경되지 않으며, 새로운 해시셋에 4와 5가 추가됩니다.
집합 연산
ImmutableHashSet
은 다양한 집합 연산을 지원합니다.
- Union: 두 해시셋을 합집합하여 새로운 해시셋을 반환합니다.
ImmutableHashSet<int> set1 = ImmutableHashSet.Create(1, 2, 3); ImmutableHashSet<int> set2 = ImmutableHashSet.Create(3, 4, 5); ImmutableHashSet<int> unionSet = set1.Union(set2); // unionSet은 1, 2, 3, 4, 5를 포함합니다.
- Intersect: 두 해시셋의 교집합을 반환합니다.
ImmutableHashSet<int> intersectSet = set1.Intersect(set2); // intersectSet은 3을 포함합니다.
- Except: 첫 번째 해시셋에서 두 번째 해시셋에 포함된 요소를 제외한 새로운 해시셋을 반환합니다.
ImmutableHashSet<int> exceptSet = set1.Except(set2); // exceptSet은 1, 2를 포함합니다.
- SymmetricExcept: 두 해시셋의 대칭 차집합을 반환합니다.
ImmutableHashSet<int> symmetricSet = set1.SymmetricExcept(set2); // symmetricSet은 1, 2, 4, 5를 포함합니다.
고급 활용과 최적화
멀티스레드 환경에서 중복 제거된 데이터 수집
멀티스레드 환경에서 데이터 수집 시 각 스레드가 중복 없이 데이터를 추가하고, 수집된 데이터의 일관성을 유지해야 하는 경우 ImmutableHashSet<T>
를 활용할 수 있습니다. 불변 특성 덕분에 여러 스레드가 데이터를 추가해도 데이터가 변경되지 않으므로 안전하게 중복을 제거할 수 있습니다.
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
public class ConcurrentDataCollector<T>
{
private ImmutableHashSet<T> _dataSet = ImmutableHashSet<T>.Empty;
public void AddData(T data)
{
ImmutableInterlocked.Update(ref _dataSet, set => set.Add(data));
}
public ImmutableHashSet<T> GetData()
{
return _dataSet;
}
}
// 사용 예제
var collector = new ConcurrentDataCollector<int>();
Parallel.For(0, 100, i =>
{
collector.AddData(i % 10); // 일부 중복 데이터를 추가하여 중복 제거 기능 테스트
});
var data = collector.GetData();
foreach (var item in data)
{
Console.WriteLine(item);
}
// 출력 예:
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 (중복 없이 고유 값만 포함)
ImmutableInterlocked.Update
를 사용하여 스레드 안전하게 데이터를 추가합니다.- 중복이 자동으로 제거되므로, 추가적인 중복 체크 없이 고유 데이터만 수집됩니다.
대규모 데이터의 고유성 유지 및 변경 이력 관리
ImmutableHashSet<T>
는 데이터가 변경될 때마다 새로운 인스턴스를 생성하므로, 변경 전후의 고유한 데이터 상태를 기록하여 필요할 때 과거의 고유 상태를 쉽게 복원할 수 있습니다.
using System;
using System.Collections.Immutable;
public class DataHistoryManager<T>
{
private ImmutableList<ImmutableHashSet<T>> _history = ImmutableList<ImmutableHashSet<T>>.Empty;
public void SaveCurrentState(ImmutableHashSet<T> currentState)
{
_history = _history.Add(currentState);
}
public ImmutableHashSet<T> GetStateAt(int version)
{
if (version < 0 || version >= _history.Count)
throw new ArgumentOutOfRangeException(nameof(version), "Invalid version");
return _history[version];
}
}
// 사용 예제
var manager = new DataHistoryManager<int>();
var initialState = ImmutableHashSet<int>.Empty.Add(1).Add(2);
manager.SaveCurrentState(initialState);
var updatedState = initialState.Add(3).Add(4);
manager.SaveCurrentState(updatedState);
var finalState = updatedState.Remove(1);
manager.SaveCurrentState(finalState);
// 특정 상태 복원
var snapshot = manager.GetStateAt(1);
foreach (var item in snapshot)
{
Console.WriteLine(item);
}
// 출력:
// 1, 2, 3, 4
- 각 상태를
ImmutableHashSet<T>
로 저장하여 변경 이력을 관리합니다. - 특정 시점의 데이터 상태를 복원할 수 있습니다.
다중 데이터 소스의 중복 없는 병합
여러 데이터 소스에서 수집된 데이터를 중복 없이 통합해야 하는 경우, ImmutableHashSet<T>
는 각 데이터 소스를 안전하게 병합하면서 중복 요소를 자동으로 제거해 줍니다.
using System;
using System.Collections.Immutable;
public class DataMerger<T>
{
public ImmutableHashSet<T> MergeSources(params ImmutableHashSet<T>[] sources)
{
var mergedSet = ImmutableHashSet<T>.Empty;
foreach (var source in sources)
{
mergedSet = mergedSet.Union(source);
}
return mergedSet;
}
}
// 사용 예제
var source1 = ImmutableHashSet<int>.Empty.Add(1).Add(2).Add(3);
var source2 = ImmutableHashSet<int>.Empty.Add(2).Add(3).Add(4);
var source3 = ImmutableHashSet<int>.Empty.Add(4).Add(5);
var merger = new DataMerger<int>();
var mergedData = merger.MergeSources(source1, source2, source3);
foreach (var item in mergedData)
{
Console.WriteLine(item);
}
// 출력:
// 1, 2, 3, 4, 5
Union
메서드를 사용하여 여러 해시셋을 병합합니다.- 중복된 요소는 자동으로 제거됩니다.
고성능 필터링 및 검색 최적화
ImmutableHashSet<T>
는 해시 기반의 내부 구조를 가지고 있으므로 고유한 데이터 집합을 빠르게 필터링하고 검색할 수 있습니다.
using System;
using System.Collections.Immutable;
using System.Linq;
public class DataFilter<T>
{
private ImmutableHashSet<T> _dataSet;
public DataFilter(ImmutableHashSet<T> dataSet)
{
_dataSet = dataSet;
}
public ImmutableHashSet<T> Filter(Func<T, bool> predicate)
{
return _dataSet.Where(predicate).ToImmutableHashSet();
}
}
// 사용 예제
var dataSet = ImmutableHashSet<int>.Empty.Add(1).Add(2).Add(3).Add(4).Add(5);
var filter = new DataFilter<int>(dataSet);
var filteredData = filter.Filter(x => x > 2);
foreach (var item in filteredData)
{
Console.WriteLine(item);
}
// 출력:
// 3, 4, 5
- LINQ와
ImmutableHashSet<T>
를 조합하여 효율적인 필터링을 수행합니다. - 해시 기반의 검색으로 대규모 데이터에서도 빠른 성능을 보입니다.