Ограничить размер очереди в .NET?

У меня есть объект Queue <T>, который я инициализировал до емкости 2, но, очевидно, это только емкость, и она продолжает расширяться по мере добавления элементов. Уже существует объект, который автоматически удаляет элемент из очереди при достижении предела, или это лучшее решение для создания моего собственного унаследованного класса?

4.08.2008 14:47:10
7 ОТВЕТОВ
РЕШЕНИЕ

Я выбрал базовую версию того, что я ищу, она не идеальна, но она будет работать, пока не появится что-то лучшее.

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);
    }
}
37
19.10.2016 09:27:01
Я слегка дополнил код вызовом из свойства Set of Limit, которое гарантирует, что размер очереди не превысил Limit - просто, хотя и больше, чем Limit, Dequeue. Помимо этого, это отличное решение, которое приятно и просто, спасибо.
Scott 7.05.2009 02:48:00
Хороший выбор при изменении кода «setter» для свойства «Limit».
Pure.Krome 27.10.2009 04:14:41
Там очень серьезное ограничение к этому классу, который Маркус Griep намекают на его ответ: так как ваш Enqueueметод объявлен как new(потому что Queue<T>.Enqueueэто не виртуальные), если кто - то бросает СВОЙ LimitedQueue<T>к Queue<T>они будут иметь возможность добавить столько же элементов , как они хотят без вашего ограничения вступления в силу. Я бы также порекомендовал перейти if (this.Count >= this.Limit)на while (this.Count >= this.Limit), просто чтобы быть в безопасности (для сценария, который я только что упомянул, например).
Dan Tao 20.11.2009 18:09:55
Если другие методы Queue <T> вызывают Enqueue (), будут вызваны оригиналы Enqueue, и это может вызвать серьезные проблемы
Louis Rhys 16.09.2011 03:03:21

Я бы порекомендовал вам открыть библиотеку C5 . В отличие от 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();
            }
        }
    }
}

Я думаю, что этот код должен делать именно то, что вы искали.

18
24.10.2008 13:51:56

Вы должны создать свой собственный класс, кольцевой буфер, вероятно, будет соответствовать вашим потребностям.

Структуры данных в .NET, которые позволяют вам указывать емкость, кроме массива, используют ее для построения внутренней структуры данных, используемой для хранения внутренних данных.

Например, для списка емкость используется для определения размера внутреннего массива. Когда вы начинаете добавлять элементы в список, он начинает заполнять этот массив с индекса 0 и выше, а когда он достигает вашей емкости, он увеличивает емкость до новой более высокой емкости и продолжает заполнять ее.

5
4.08.2008 14:56:49

Почему бы вам не использовать массив размером 2? Предполагается, что очередь может динамически расти и уменьшаться.

Или создайте класс-оболочку для экземпляра Queue<T>экземпляра, и каждый раз, когда кто-либо помещает <T>объект в очередь, проверяет размер очереди. Если больше 2, удалите из очереди первый элемент.

3
15.12.2015 19:44:41

Что ж, я надеюсь, что этот класс поможет вам:
Внутренний круговой буфер 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
}
3
7.02.2016 00:37:23
я думаю, что с помощью этого кода вы можете иметь очередь ограниченного размера .. которая также является кольцевым буфером.
Robel.E 15.11.2011 12:46:45

Одновременное решение

public class LimitedConcurrentQueue<ELEMENT> : ConcurrentQueue<ELEMENT>
{
    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);
        }
    }
}

Примечание: Поскольку Enqueueконтролирует добавление элементов и делает это по одному, выполнять whilefor не нужно TryDequeue.

3
9.05.2018 20:39:16

Если это кому-нибудь пригодится, я сделал 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; }
    }
}

Он удаляет самый старый элемент (низ стопки), когда он становится слишком большим.

(Этот вопрос был лучшим результатом Google для "предельного размера стека C #")

1
15.01.2012 05:28:40
Этот код на 99% правильный. Однако, если мы вызовем Peek или Pop без помещения чего-либо в стек, он потерпит крах, так как индекс равен -1. Это можно легко исправить, добавив проверку границ индекса.
Contango 19.06.2014 09:51:05
Предложите добавить в Peek и Pop () следующее: if ((_stack.Count - 1) <0) выдает новое исключение («Can Peek или Pop без первого нажатия.») ;. Это предупредит программиста об этом угловом случае и позволит им помнить об этом при использовании этого класса. Мы также могли бы добавить TryPeek или TryPop, который Microsoft использует в своих реализациях ConcurrentDictionary.
Contango 19.06.2014 09:53:16
Напомним, что этот код не является потокобезопасным без дополнительной блокировки (что совершенно нормально, безопасность потоков никогда не была частью спецификации проекта для этого класса).
Contango 19.06.2014 10:18:56