在.NET中限制Queue <T>的大小?

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
额外
意见: 1

7 答案

为什么不使用大小为2的数组?队列应该能够动态增长和缩小。

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
额外

我已经敲定了我正在寻找的基本版本,但它并不完美,但它会完成这项工作,直到有更好的产品出现。

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
额外
改变'限制'属性的'setter'代码的好处。
额外 作者 Pure.Krome,
由于您的 Enqueue 方法被声明为 new (因为 Queue ),所以Marcus Griep在他的回答中暗示了这个类有一个非常严重的限制。 Enqueue 不是虚拟的),如果有人将您的 LimitedQueue 转换为 Queue ,他们将能够添加尽可能多的项目希望没有你的限制生效。我还建议在(this.Count> = this.Limit)时将 if(this.Count> = this.Limit)更改为,只是为了安全起见(例如,我刚刚提到的场景)。
额外 作者 Dan Tao,
如果队列的其他方法调用Enqueue(),则会调用原始Enqueue,并且可能导致严重问题
额外 作者 Louis Rhys,
我使用Limit属性集中的调用稍微增加了代码,以确保队列大小没有超过限制 - 只是一个简单的大于Limit,Dequeue。除此之外,这是一个很好的解决方案,非常简单,谢谢。
额外 作者 Scott,

你应该创建自己的班级,一个环缓冲可能会适合你的需求。

.NET中允许您指定容量的数据结构(数组除外)用于构建用于容纳内部数据的内部数据结构。

例如,对于列表,容量用于调整内部数组的大小。当你开始向列表添加元素时,它将开始从索引0开始填充这个数组,并且当它达到你的容量时,它将容量增加到一个新的更高容量,并且继续填充它。

0
额外

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

当它变得太大时,它会删除最旧的项目(堆栈底部)。

(这个问题是谷歌对“C#限制堆栈大小”的最高结果)

0
额外
这段代码是99%正确的。但是,如果我们调用Peek或Pop而不将任何东西放在堆栈上,它将会随着索引为-1而崩溃。这可以通过添加索引边界检查很容易地解决。
额外 作者 Contango,
建议在Peek和Pop()中添加以下内容:if((_stack.Count - 1)<0)抛出新的异常(“不能先执行Push时不能窥视或流行”)。这会提醒程序员这个角落案例,并让他们在使用这个类时记住它。我们还可以添加TryPeek或TryPop,这是Microsoft采用ConcurrentDictionary实现所采用的方法。
额外 作者 Contango,
为了记录,这段代码在没有附加锁定的情况下不是线程安全的(这是绝对好的,线程安全从来不是这个类的设计规范的一部分)。
额外 作者 Contango,

并发解决方案

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

注意:由于 Enqueue 控制元素的添加,并且每次只执行一个元素,所以不需要为 TryDequeue 执行,而

0
额外

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

我认为这段代码应该完全符合你的要求。

0
额外

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.

注意:您不能随意移除项目。我将方法Remove(T item)设置为返回false。 如果你想要你可以修改以随机删除项目

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
额外
我想通过使用这个代码你可以有一个有限的队列......这也是循环缓冲区。
额外 作者 Robel.E,