Limiter la taille de la file d'attente <T> dans .NET?

I have a Queue object that I have initialised to a capacity of 2, but obviously that is just the capacity and it keeps expanding as I add items. Is there already an object that automatically dequeues an item when the limit is reached, or is the best solution to create my own inherited class?

0

7 Réponses

Pourquoi n'utiliserais-tu pas un tableau avec une taille de 2? Une file d'attente est censée pouvoir croître et rétrécir de façon dynamique.

Or create a wrapper class around an instance of Queue instance and each time one enqueues a object, check the size of the queue. If larger than 2, dequeue the first item.

0
ajouté

J'ai corrigé une version basique de ce que je cherchais, ce n'est pas parfait mais ça va faire le boulot jusqu'à ce qu'il y ait quelque chose de mieux.

public class LimitedQueue : Queue
{
    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
ajouté
Bon ramassage en changeant le code 'setter' pour la propriété 'Limit'.
ajouté l'auteur Pure.Krome, source
Il y a une très sérieuse limitation à cette classe, à laquelle Marcus Griep a fait allusion dans sa réponse: puisque votre méthode Enqueue est déclarée comme new (parce que Queue . Enqueue n'est pas virtuel), si quelqu'un jette votre LimitedQueue à une file d'attente , il pourra ajouter autant d'éléments qu'il le souhaite. vouloir sans que votre limite prenne effet. Je recommande également de changer if (this.Count> = this.Limit) en while (this.Count> = this.Limit) , juste pour êtr
ajouté l'auteur Dan Tao, source
Si les autres méthodes de la file d'attente appelle Enqueue (), les originaux Enqueue seront appelés et cela peut causer de sérieux problèmes
ajouté l'auteur Louis Rhys, source
J'ai légèrement augmenté le code avec un appel provenant de la propriété Set of the Limit qui garantit que la taille de la file d'attente n'a pas dépassé la limite - juste un simple supérieur à la limite, Dequeue. A part ça, c'est une bonne solution, c'est gentil et simple, merci.
ajouté l'auteur Scott, source

Vous devriez créer votre propre classe, un ringbuffer répondrait probablement à vos besoins.

Les structures de données dans .NET qui vous permettent de spécifier la capacité, à l'exception de array, l'utilisent pour construire la structure de données interne utilisée pour contenir les données internes.

Par exemple, pour une liste, la capacité est utilisée pour dimensionner un tableau interne. Lorsque vous commencez à ajouter des éléments à la liste, il commence à remplir ce tableau à partir de l'index 0 et plus, et quand il atteint votre capacité, il augmente la capacité à une nouvelle capacité plus élevée, et continue à le remplir.

0
ajouté

If it's of any use to anyone, I made a LimitedStack.

public class LimitedStack
{
    public readonly int Limit;
    private readonly List _stack;

    public LimitedStack(int limit = 32)
    {
        Limit = limit;
        _stack = new List(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; }
    }
}

Il supprime l'élément le plus ancien (bas de la pile) lorsqu'il devient trop gros.

(Cette question est le résultat le plus élevé de Google pour "C # limit stack size")

0
ajouté
Ce code est correct à 99%. Cependant, si nous appelons Peek ou Pop sans mettre quoi que ce soit sur la pile, il se bloquera car l'index est -1. Cela pourrait être facilement résolu en ajoutant une vérification des limites d'index.
ajouté l'auteur Contango, source
Suggérer d'ajouter ce qui suit à Peek et Pop (): if ((_stack.Count - 1) <0) lancer une nouvelle exception ("Impossible de jeter un coup d'œil ou de pop sans faire d'abord un push.") ;. Cela alerterait le programmeur à ce cas de coin, et leur permettrait de garder à l'esprit lors de l'utilisation de cette classe. Nous pourrions également ajouter TryPeek ou TryPop, ce que Microsoft a fait avec ses implémentations de ConcurrentDictionary.
ajouté l'auteur Contango, source
Pour l'anecdote, ce code n'est pas thread-safe sans verrouillage supplémentaire (ce qui est absolument parfait, la sécurité des threads n'a jamais fait partie des spécifications de conception pour cette classe).
ajouté l'auteur Contango, source

Solution simultanée

public class LimitedConcurrentQueue : ConcurrentQueue
{
    public readonly int Limit;

    public LimitedConcurrentQueue(int limit)
    {
        Limit = limit;
    }

    public new void Enqueue(ELEMENT element)
    {
        base.Enqueue(element);
        if (Count > Limit)
        {
            TryDequeue(out ELEMENT discard);
        }
    }
}

Note: Puisque Enqueue contrôle l'ajout d'éléments, et ce, un à la fois, il n'est pas nécessaire d'exécuter while pour TryDequeue .

0
ajouté

I would recommend that you pull up the C5 Library. Unlike SCG (System.Collections.Generic), C5 is programmed to interface and designed to be subclassed. Most public methods are virtual and none of the classes are sealed. This way, you won't have to use that icky "new" keyword which wouldn't trigger if your LimitedQueue were cast to a SCG.Queue. With C5 and using close to the same code as you had before, you would derive from the CircularQueue. The CircularQueue actually implements both a stack and a queue, so you can get both options with a limit nearly for free. I've rewritten it below with some 3.5 constructs:

using C5;

public class LimitedQueue : CircularQueue
{
    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();
            }
        }
    }
}

Je pense que ce code devrait faire exactement ce que vous cherchiez.

0
ajouté

Well I hope this class will helps You:
Internally the Circular FIFO Buffer use a Queue with the specified size. Once the size of the buffer is reached, it will replaces older items with new ones.

REMARQUE: vous ne pouvez pas supprimer des éléments de manière aléatoire. J'ai mis la méthode Remove (T item) pour retourner false. Si vous voulez Vous pouvez modifier pour supprimer des éléments au hasard

public class CircularFIFO : ICollection , IDisposable
{
    public Queue CircularBuffer;

    /// 
/// The default initial capacity. ///
 
    private int capacity = 32;

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

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

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

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

        CircularBuffer.Enqueue(item);
    }

    /// 
/// Adds array of items to the end of the FIFO. ///
 
    /// 
 The array of items to add to the end of the FIFO. 
     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]);
        }           
    }

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

        return removedItem;
    }

    /// 
/// Removes and Returns the array of items form the FIFO. ///
 
    /// 
 The size of item to be removed from the FIFO. 
    ///  Removed array of items 
    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;
    }

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

    /// 
/// Returns the array of item at the beginning of the FIFO with out removing it. ///
 
    /// 
 The size of the array items. 
    ///  Array of peeked items. 
    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;
    }

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

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

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

    #region ICollection Members

    /// 
/// Determines whether an element is in the FIFO. ///
 
    /// 
 The item to locate in the FIFO. 
    /// 
    public bool Contains(T item)
    {
        return CircularBuffer.Contains(item);
    }

    /// 
/// Copies the FIFO elements to an existing one-dimensional array. ///
 
    /// 
The one-dimensional array that have at list a size of the FIFO 
    /// 
    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 Members

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

    #endregion

    #region IEnumerable Members

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

    #endregion

    #region IDisposable Members

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

    #endregion
}
0
ajouté
Je pense qu'en utilisant ce code, vous pouvez avoir une file d'attente de taille limitée, qui est aussi un tampon circulaire.
ajouté l'auteur Robel.E, source