2008-08-04 11 views
49

Tengo un objeto Queue <T> que he inicializado a una capacidad de 2, pero obviamente esa es solo la capacidad y sigue expandiéndose a medida que agrego elementos. ¿Ya hay un objeto que dequeue automáticamente un artículo cuando se alcanza el límite, o es la mejor solución para crear mi propia clase heredada?¿Limitar el tamaño de la cola <T> en .NET?

Respuesta

31

He alterado una versión básica de lo que estoy buscando, no es perfecto, pero hará el trabajo hasta que surja algo mejor.

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

que aumentó el código levemente con una llamada desde dentro del conjunto de la propiedad límite que asegura que el tamaño de la cola no ha superado el Límite: solo un simple Mientras mayor que Límite, Dequeue. Aparte de eso, esta es una gran solución que es agradable y simple, gracias. – Scott

+0

Buena activación al cambiar el código 'setter' para la propiedad 'Limitar'. –

+17

Hay una limitación muy seria para esta clase, que Marcus Griep insinuó en su respuesta: ya que su método 'Enqueue' se declara como' nuevo' (porque 'Queue .Enqueue' no es virtual), si alguien lanza su' LimitedQueue 'a' Queue 'podrán agregar tantos elementos como deseen sin que su límite entre en vigencia. También recomendaría cambiar 'if (this.Count> = this.Limit)' a 'while (this.Count> = this.Limit)', solo para estar seguro (para el escenario que acabo de mencionar, por ejemplo) –

3

¿Por qué no debería simplemente usar una matriz con un tamaño de 2? Se supone que una cola puede crecer y contraerse dinámicamente.

O crea una clase contenedora alrededor de una instancia de instancia Queue<T> y cada vez que uno pone en cola un objeto <T>, verifica el tamaño de la cola. Si es mayor que 2, dequeue el primer elemento.

5

Debe crear su propia clase, un Ringbuffer probablemente se ajuste a sus necesidades.

Las estructuras de datos en .NET que le permiten especificar la capacidad, a excepción de la matriz, la usa para construir la estructura de datos interna utilizada para contener los datos internos.

Por ejemplo, para una lista, la capacidad se usa para dimensionar una matriz interna. Cuando comience a agregar elementos a la lista, comenzará a llenar esta matriz desde el índice 0 y hacia arriba, y cuando alcance su capacidad, aumentará la capacidad a una nueva capacidad más alta y continuará llenándola.

15

Yo recomendaría que levante el C5 Library. A diferencia de SCG (System.Collections.Generic), C5 está programado para interfaz y diseñado para ser subclassed. La mayoría de los métodos públicos son virtuales y ninguna de las clases está sellada. De esta manera, no tendrá que usar esa palabra clave icky "nueva" que no se activaría si su LimitedQueue<T> fuera lanzado a SCG.Queue<T>. Con C5 y usando casi el mismo código que tenía antes, derivaría del CircularQueue<T>. El CircularQueue<T> realmente implementa tanto una pila como una cola, por lo que puede obtener ambas opciones con un límite casi gratuito. He reescrito por debajo de 3,5 con algunas construcciones:

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(); 
      } 
     } 
    } 
} 

creo que este código debe hacer exactamente lo que estaba buscando.

3

Bueno espero que esto le ayuda a la clase:
Internamente, la FIFO buffer circular utilizar una cola <T> con el tamaño especificado. Una vez que se alcanza el tamaño del búfer, reemplazará los elementos más viejos por otros nuevos.

NOTA: No puede eliminar elementos aleatoriamente. Configuro el método Eliminar (elemento T) para devolver falso. si lo desea, puede modificar para eliminar los elementos al azar

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

Creo que al usar este código puede tener una cola de tamaño limitado ... que también es un buffer circular. –

1

Si es de ninguna utilidad para nadie, hice un 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; } 
    } 
} 

Quita el elemento más antiguo (parte inferior de la pila) cuando se vuelve demasiado grande.

(Esta pregunta fue la parte superior resultado en Google de "C# límite de tamaño de la pila")

+0

Este código es 99% correcto. Sin embargo, si llamamos a Peek o Pop sin poner nada en la pila, se bloqueará ya que el índice es -1. Esto podría solucionarse fácilmente agregando la verificación de límites de índice. – Contango

+0

Sugerir agregar lo siguiente a Peek and Pop(): \t \t \t if ((_stack.Count - 1) <0) arrojar nueva excepción ("No puedo mirar ni hacer estallar sin antes hacer un push.") ;. Esto alertaría al programador sobre este caso de esquina y les permitiría tenerlo en cuenta al usar esta clase. También podríamos agregar TryPeek o TryPop, que es el enfoque que Microsoft ha adoptado con sus implementaciones ConcurrentDictionary. – Contango

+1

Para el registro, este código no es seguro para hilos sin bloqueo adicional (lo cual es absolutamente correcto, la seguridad del hilo nunca fue parte de las especificaciones de diseño para esta clase). – Contango

Cuestiones relacionadas