ImmutableQueue

ImmutableQueue<T>는 .NET에서 제공하는 불변 컬렉션Immutable Collections 중 하나로, 변경 불가능한 를 관리합니다. 불변 큐는 생성된 이후에 상태가 변경되지 않으며, 데이터의 추가나 삭제가 필요할 때마다 새로운 큐를 생성합니다. 이러한 특성은 데이터 무결성을 보장하고, 멀티스레드 환경에서의 동시성 문제를 방지하는 데 큰 장점이 있습니다.

불변 큐의 특징

  • 변경 불가능성: 큐가 생성된 이후에는 상태가 변경되지 않습니다. 데이터를 추가하거나 삭제할 때마다 새로운 큐가 생성됩니다.
  • 구조적 공유: 새로운 큐를 생성할 때 전체 데이터를 복사하지 않고, 변경된 부분만 복사하여 메모리 사용을 최적화합니다.
  • 스레드 안전성: 불변 큐는 멀티스레드 환경에서 안전하게 사용할 수 있어, 동기화에 대한 고민을 덜어줍니다.

기본 사용법

using System;
using System.Collections.Immutable;
ImmutableQueue<int> queue = ImmutableQueue<int>.Empty;
queue = queue.Enqueue(1);
queue = queue.Enqueue(2);
queue = queue.Enqueue(3);
Console.WriteLine(string.Join(", ", queue));
// 출력: 1, 2, 3
queue = queue.Dequeue(out int value);
Console.WriteLine(value); // 출력: 1
Console.WriteLine(string.Join(", ", queue));
// 출력: 2, 3
  • ImmutableQueue<int>.Empty를 사용하여 빈 큐를 생성합니다.
  • Enqueue를 호출하여 요소를 추가하면 새로운 큐가 반환됩니다.
  • Dequeue를 호출하여 요소를 제거하면 새로운 큐와 제거된 요소를 반환합니다.
  • 기존 큐는 변경되지 않고 그대로 유지됩니다.

주요 메서드와 활용

생성 및 초기화

// 빈 큐 생성
ImmutableQueue<int> emptyQueue = ImmutableQueue<int>.Empty;
// 요소를 추가하여 큐 생성
ImmutableQueue<int> queue = emptyQueue.Enqueue(1).Enqueue(2).Enqueue(3);

요소 추가 및 제거

  • Enqueue: 큐의 끝에 요소를 추가하여 새로운 큐를 반환합니다.
    var newQueue = queue.Enqueue(4);
  • Dequeue: 큐의 앞에서 요소를 제거하고 새로운 큐를 반환하며, 제거된 요소를 출력 변수로 반환합니다.
    var updatedQueue = queue.Dequeue(out int removedValue);
  • Peek: 큐의 앞에 있는 요소를 제거하지 않고 반환합니다.
    int front = queue.Peek();
  • IsEmpty: 큐가 비어 있는지 여부를 확인합니다.
    bool isEmpty = queue.IsEmpty;

시계열 데이터 기록 및 순차적 참조

ImmutableDictionary<int, ImmutableQueue<T>>를 사용하여 각 시점의 데이터를 기록하고, 인덱스를 통해 특정 시점의 데이터를 효율적으로 복원할 수 있습니다.

  • 시점별 데이터 기록 및 빠른 복원: 각 시점의 데이터를 불변 딕셔너리에 저장하여, 필요할 때마다 인덱스로 특정 시점의 데이터를 빠르게 참조할 수 있습니다.
  • 데이터의 불변성 유지: 시점별 데이터를 큐에 저장하고, 딕셔너리를 통해 참조하여 여러 스레드가 접근해도 데이터 일관성을 유지할 수 있습니다.
using System;
using System.Collections.Immutable;
public class TimeSeriesData<T>
{
    private ImmutableDictionary<int, ImmutableQueue<T>> _timeSeriesData = ImmutableDictionary<int, ImmutableQueue<T>>.Empty;
    private ImmutableQueue<T> _currentQueue = ImmutableQueue<T>.Empty;
    public void RecordData(T data)
    {
        _currentQueue = _currentQueue.Enqueue(data); // 새로운 데이터를 큐에 추가
        int index = _timeSeriesData.Count; // 현재 딕셔너리 항목 수를 인덱스로 사용
        _timeSeriesData = _timeSeriesData.SetItem(index, _currentQueue); // 딕셔너리에 새로운 큐 상태 저장
    }
    public ImmutableQueue<T> GetSnapshotAt(int index)
    {
        if (_timeSeriesData.TryGetValue(index, out var dataSnapshot))
        {
            return dataSnapshot; // 해당 시점의 데이터 스냅샷 반환
        }
        throw new ArgumentOutOfRangeException(nameof(index), "Invalid snapshot index");
    }
    public ImmutableDictionary<int, ImmutableQueue<T>> GetAllSnapshots()
    {
        return _timeSeriesData; // 전체 시점별 데이터 스냅샷 반환
    }
}
// 사용 예제
var timeSeriesManager = new TimeSeriesData<int>();
timeSeriesManager.RecordData(100); // 시점 1의 데이터 추가
timeSeriesManager.RecordData(200); // 시점 2의 데이터 추가
timeSeriesManager.RecordData(300); // 시점 3의 데이터 추가
// 특정 시점 데이터 복원
var snapshot = timeSeriesManager.GetSnapshotAt(1); // 시점 2의 데이터 가져오기
foreach (var dataPoint in snapshot)
{
    Console.WriteLine(dataPoint);
}
// 출력:
// 100
// 200
  • 새 데이터를 ImmutableQueue<T>에 추가하여 새로운 큐 인스턴스를 생성합니다.
  • 딕셔너리의 현재 항목 수를 인덱스로 사용하여 ImmutableDictionary<int, ImmutableQueue<T>>에 새로운 큐 상태를 저장합니다.
  • 데이터를 추가할 때마다 자동으로 인덱스가 증가하여 시점별 데이터가 저장됩니다.