2008-08-04 4 views
49

나는 용량이 2로 초기화 된 큐 <T> 개체를 가지고 있지만 분명히 그 개체는 용량이고 계속 항목을 추가 할 때 계속 확장됩니다. 한계에 도달했을 때 자동으로 항목을 대기열에서 제외하거나 이미 상속 된 클래스를 만들 때 가장 좋은 해결책 인 객체가 이미 있습니까?.NET의 대기열 크기 제한 <T>?

답변

31

내가 찾고있는 기본 버전을 사용했는데 완벽하지는 않지만 더 나은 기능이 제공 될 때까지 작업을 수행 할 것입니다.

public class LimitedQueue<T> : Queue<T> 
{ 
    public int Limit { get; set; } 

    public LimitedQueue(int limit) : base(limit) 
    { 
     Limit = limit; 
    } 

    public new void Enqueue(T item) 
    { 
     while (Count >= Limit) 
     { 
      Dequeue(); 
     } 
     base.Enqueue(item); 
    } 
} 
+0

(이 질문은 "C# 제한 스택 크기"에 대한 상단 구글 결과였다) 한도 - 단순한 한도 초과, 데 큐잉. 그 외,이 솔루션은 멋지고 간단합니다. 감사합니다. – Scott

+0

'제한'속성의 '설정자'코드를 변경하는 것이 좋습니다. –

+17

이 클래스에는 Marcus Griep이 매우 암시 한 제한이 있습니다 :'Enqueue' 메쏘드가'New'로 선언 되었기 때문에 (Queue .Enqueue는 가상이 아닙니다.) 누군가가 당신의'LimitQueue '을 '대기열 '에 추가하면 제한을 적용하지 않고 원하는만큼 항목을 추가 할 수 있습니다. 나는 또한'if (this.Count> = this.Limit)'를'while (this.Count> = this.Limit)'으로 변경하는 것을 권하고 싶다. 단지 안전한면에있다. (방금 언급 한 시나리오에서 예를 들어). –

3

왜 크기가 2 인 배열을 사용하지 않습니까? 대기열은 동적으로 성장하고 축소 할 수 있어야합니다.

Queue<T> 인스턴스 주위에 래퍼 클래스를 만들고 <T> 개체를 대기열에 추가 할 때마다 큐의 크기를 확인하십시오. 2보다 큰 경우 첫 번째 항목을 큐에서 빼냅니다.

5

자신 만의 클래스를 만들어야합니다. 링 버퍼가 사용자의 필요에 맞을 것입니다.

배열을 제외한 용량을 지정할 수있는 .NET의 데이터 구조는이 데이터를 사용하여 내부 데이터를 보유하는 데 사용되는 내부 데이터 구조를 작성합니다.

예를 들어, 목록의 경우 용량은 내부 배열의 크기를 지정하는 데 사용됩니다. 목록에 요소를 추가하기 시작하면 인덱스 0부터 배열에 채우기를 시작하고 용량에 도달하면 용량을 새로운 높은 용량으로 늘리고 계속 채 웁니다.

15

C5 Library을 가져 오는 것이 좋습니다. SCG (System.Collections.Generic)와 달리 C5는 인터페이스를 프로그래밍하고 서브 클래 싱되도록 설계되었습니다. 대부분의 공용 메소드는 가상이며 클래스 중 하나도 봉인되지 않습니다. 이렇게하면 LimitedQueue<T>SCG.Queue<T>으로 캐스팅 된 경우 트리거되지 않는 칙칙한 "새"키워드를 사용할 필요가 없습니다. C5를 사용하고 이전과 동일한 코드를 사용하면 CircularQueue<T>에서 파생됩니다. CircularQueue<T>은 실제로 스택과 대기열을 모두 구현하므로 거의 두 가지 옵션 모두 무료로 사용할 수 있습니다. 아래에 몇 가지 3.5 구문으로 다시 작성했습니다.

using C5; 

public class LimitedQueue<T> : CircularQueue<T> 
{ 
    public int Limit { get; set; } 

    public LimitedQueue(int limit) : base(limit) 
    { 
     this.Limit = limit; 
    } 

    public override void Push(T item) 
    { 
     CheckLimit(false); 
     base.Push(item); 
    } 

    public override void Enqueue(T item) 
    { 
     CheckLimit(true); 
     base.Enqueue(item); 
    } 

    protected virtual void CheckLimit(bool enqueue) 
    { 
     while (this.Count >= this.Limit) 
     { 
      if (enqueue) 
      { 
       this.Dequeue(); 
      } 
      else 
      { 
       this.Pop(); 
      } 
     } 
    } 
} 

이 코드는 사용자가 찾고 있던 것과 정확히 일치해야한다고 생각합니다.

3

이 잘 나는이 클래스의 뜻은 당신을 도움이되기를 바랍니다 :
내부적으로 원형 FIFO 버퍼 사이즈가 지정된 큐 <T>를 사용합니다. 버퍼의 크기에 도달하면 이전 항목이 새로운 항목으로 바뀝니다.

참고 : 임의로 항목을 제거 할 수는 없습니다. Remove (T item) 메서드를 false로 설정했습니다. 당신이 원하는 경우에 당신은이 사람에게 어떤 소용이 있다면 무작위로

public class CircularFIFO<T> : ICollection<T> , IDisposable 
{ 
    public Queue<T> CircularBuffer; 

    /// <summary> 
    /// The default initial capacity. 
    /// </summary> 
    private int capacity = 32; 

    /// <summary> 
    /// Gets the actual capacity of the FIFO. 
    /// </summary> 
    public int Capacity 
    { 
     get { return capacity; }   
    } 

    /// <summary> 
    /// Initialize a new instance of FIFO class that is empty and has the default initial capacity. 
    /// </summary> 
    public CircularFIFO() 
    {    
     CircularBuffer = new Queue<T>(); 
    } 

    /// <summary> 
    /// Initialize a new instance of FIFO class that is empty and has the specified initial capacity. 
    /// </summary> 
    /// <param name="size"> Initial capacity of the FIFO. </param> 
    public CircularFIFO(int size) 
    { 
     capacity = size; 
     CircularBuffer = new Queue<T>(capacity); 
    } 

    /// <summary> 
    /// Adds an item to the end of the FIFO. 
    /// </summary> 
    /// <param name="item"> The item to add to the end of the FIFO. </param> 
    public void Add(T item) 
    { 
     if (this.Count >= this.Capacity) 
      Remove(); 

     CircularBuffer.Enqueue(item); 
    } 

    /// <summary> 
    /// Adds array of items to the end of the FIFO. 
    /// </summary> 
    /// <param name="item"> The array of items to add to the end of the FIFO. </param> 
    public void Add(T[] item) 
    { 
     int enqueuedSize = 0; 
     int remainEnqueueSize = this.Capacity - this.Count; 

     for (; (enqueuedSize < item.Length && enqueuedSize < remainEnqueueSize); enqueuedSize++) 
      CircularBuffer.Enqueue(item[enqueuedSize]); 

     if ((item.Length - enqueuedSize) != 0) 
     { 
      Remove((item.Length - enqueuedSize));//remaining item size 

      for (; enqueuedSize < item.Length; enqueuedSize++) 
       CircularBuffer.Enqueue(item[enqueuedSize]); 
     }   
    } 

    /// <summary> 
    /// Removes and Returns an item from the FIFO. 
    /// </summary> 
    /// <returns> Item removed. </returns> 
    public T Remove() 
    { 
     T removedItem = CircularBuffer.Peek(); 
     CircularBuffer.Dequeue(); 

     return removedItem; 
    } 

    /// <summary> 
    /// Removes and Returns the array of items form the FIFO. 
    /// </summary> 
    /// <param name="size"> The size of item to be removed from the FIFO. </param> 
    /// <returns> Removed array of items </returns> 
    public T[] Remove(int size) 
    { 
     if (size > CircularBuffer.Count) 
      size = CircularBuffer.Count; 

     T[] removedItems = new T[size]; 

     for (int i = 0; i < size; i++) 
     { 
      removedItems[i] = CircularBuffer.Peek(); 
      CircularBuffer.Dequeue(); 
     } 

     return removedItems; 
    } 

    /// <summary> 
    /// Returns the item at the beginning of the FIFO with out removing it. 
    /// </summary> 
    /// <returns> Item Peeked. </returns> 
    public T Peek() 
    { 
     return CircularBuffer.Peek(); 
    } 

    /// <summary> 
    /// Returns the array of item at the beginning of the FIFO with out removing it. 
    /// </summary> 
    /// <param name="size"> The size of the array items. </param> 
    /// <returns> Array of peeked items. </returns> 
    public T[] Peek(int size) 
    { 
     T[] arrayItems = new T[CircularBuffer.Count]; 
     CircularBuffer.CopyTo(arrayItems, 0); 

     if (size > CircularBuffer.Count) 
      size = CircularBuffer.Count; 

     T[] peekedItems = new T[size]; 

     Array.Copy(arrayItems, 0, peekedItems, 0, size); 

     return peekedItems; 
    } 

    /// <summary> 
    /// Gets the actual number of items presented in the FIFO. 
    /// </summary> 
    public int Count 
    { 
     get 
     { 
      return CircularBuffer.Count; 
     } 
    } 

    /// <summary> 
    /// Removes all the contents of the FIFO. 
    /// </summary> 
    public void Clear() 
    { 
     CircularBuffer.Clear(); 
    } 

    /// <summary> 
    /// Resets and Initialize the instance of FIFO class that is empty and has the default initial capacity. 
    /// </summary> 
    public void Reset() 
    { 
     Dispose(); 
     CircularBuffer = new Queue<T>(capacity); 
    } 

    #region ICollection<T> Members 

    /// <summary> 
    /// Determines whether an element is in the FIFO. 
    /// </summary> 
    /// <param name="item"> The item to locate in the FIFO. </param> 
    /// <returns></returns> 
    public bool Contains(T item) 
    { 
     return CircularBuffer.Contains(item); 
    } 

    /// <summary> 
    /// Copies the FIFO elements to an existing one-dimensional array. 
    /// </summary> 
    /// <param name="array"> The one-dimensional array that have at list a size of the FIFO </param> 
    /// <param name="arrayIndex"></param> 
    public void CopyTo(T[] array, int arrayIndex) 
    { 
     if (array.Length >= CircularBuffer.Count) 
      CircularBuffer.CopyTo(array, 0);   
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool Remove(T item) 
    { 
     return false; 
    } 

    #endregion 

    #region IEnumerable<T> Members 

    public IEnumerator<T> GetEnumerator() 
    { 
     return CircularBuffer.GetEnumerator(); 
    } 

    #endregion 

    #region IEnumerable Members 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return CircularBuffer.GetEnumerator(); 
    } 

    #endregion 

    #region IDisposable Members 

    /// <summary> 
    /// Releases all the resource used by the FIFO. 
    /// </summary> 
    public void Dispose() 
    {   
     CircularBuffer.Clear(); 
     CircularBuffer = null; 
     GC.Collect(); 
    } 

    #endregion 
} 
+1

이 코드를 사용하면 제한된 크기의 큐를 가질 수 있다고 생각합니다. 또한 Circular 버퍼입니다. –

1

를 항목을 제거하기 위해 수정할 수있는, 내가 LimitedStack<T>했다.

public class LimitedStack<T> 
{ 
    public readonly int Limit; 
    private readonly List<T> _stack; 

    public LimitedStack(int limit = 32) 
    { 
     Limit = limit; 
     _stack = new List<T>(limit); 
    } 

    public void Push(T item) 
    { 
     if (_stack.Count == Limit) _stack.RemoveAt(0); 
     _stack.Add(item); 
    } 

    public T Peek() 
    { 
     return _stack[_stack.Count - 1]; 
    } 

    public void Pop() 
    { 
     _stack.RemoveAt(_stack.Count - 1); 
    } 

    public int Count 
    { 
     get { return _stack.Count; } 
    } 
} 

너무 커지면 가장 오래된 항목 (스택 하단)이 제거됩니다.

내가 큐 크기가 초과되지 않았 음을 보장 제한 등록 정보의 설정 내에서 호출 코드를 약간 증강

+0

이 코드는 99 % 정확합니다. 그러나 Peek 또는 Pop을 스택에 넣지 않고 호출하면 인덱스가 -1 일 때 충돌이 발생합니다. 인덱스 경계 검사를 추가하면이 문제를 쉽게 해결할 수 있습니다. – Contango

+0

Peek 및 Pop()에 다음을 추가 할 것을 제안하십시오. \t \t \t if ((_stack.Count - 1) <0) throw new exception ("처음 푸시하지 않고 Peek 또는 Pop을 할 수 없습니다."). 이것은 프로그래머에게이 코너 케이스를 경고하고,이 클래스를 사용할 때 명심해야한다. TryPeek 또는 TryPop을 추가 할 수도 있습니다. TryPeek는 Microsoft가 ConcurrentDictionary 구현을 통해 취한 접근 방식입니다. – Contango

+1

레코드의 경우이 코드는 추가 잠금 없이는 스레드 안전하지 않습니다 (이는 절대적으로 좋으며 스레드 안전은이 클래스의 디자인 사양의 일부가 아닙니다). – Contango