2008-08-04 15 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#リミットスタックサイズ」のトップのGoogle結果でした) Limit - 単純な単純な制限を超えて、デキュー。 これ以外にも、これは素敵でシンプルな、素晴らしいソリューションです。 – Scott

+0

'Limit'プロパティの 'setter'コードを変更すると良いピックアップです。 –

+17

このクラスには非常に深刻な制限があります。Marcus Griepは答えました。あなたの 'Enqueue'メソッドは' New'と宣言されています( 'Queue .Enqueue'は仮想ではありません)。もし誰かが' LimitQueue 'を' Queue 'に変更すると、制限を有効にすることなく、できるだけ多くの項目を追加できます。また、 '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(); 
      } 
     } 
    } 
} 

私はこのコードは、あなたが探していた正確に何をすべきだと思います。
内部循環FIFOバッファ指定されたサイズでキュー<T>を使用します。

3

まあ、私はこのクラスの意志があなたを助け願っています。 バッファのサイズに達すると、古い項目が新しい項目に置き換えられます。

注:ランダムにアイテムを削除することはできません。 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

このコードを使用すると、キューのサイズが制限されている可能性があります。 –

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を追加することもできます。これはMicrosoftがConcurrentDictionaryの実装で取ったアプローチです。 – Contango

+1

レコードの場合、このコードは追加のロックなしではスレッドセーフではありません(スレッド安全は決してこのクラスの設計仕様の一部ではありません)。 – Contango