限制 .NET 中 Queue<T> 的大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Limit size of Queue<T> in .NET?
提问by tags2k
I have a Queue<T> 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?
我有一个 Queue<T> 对象,我已将其初始化为 2 的容量,但显然这只是容量,并且随着我添加项目而不断扩展。是否已经有一个对象在达到限制时自动使项目出列,或者是创建我自己的继承类的最佳解决方案?
采纳答案by tags2k
I've knocked up a basic version of what I'm looking for, it's not perfect but it'll do the job until something better comes along.
我已经找到了我正在寻找的基本版本,它并不完美,但它会完成工作,直到出现更好的东西。
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);
}
}
回答by Marcus Griep
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<T>were cast to a SCG.Queue<T>. With C5 and using close to the same code as you had before, you would derive from the CircularQueue<T>. The CircularQueue<T>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:
我建议你拉起C5 库。与 SCG (System.Collections.Generic) 不同,C5 被编程为接口并设计为子类。大多数公共方法都是虚拟的,并且没有一个类是密封的。这样,您就不必使用那个讨厌的“new”关键字,如果您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();
}
}
}
}
I think that this code should do exactly what you were looking for.
我认为这段代码应该完全符合您的要求。
回答by Lasse V. Karlsen
You should create your own class, a ringbuffer would probably fit your needs.
您应该创建自己的类,环形缓冲区可能适合您的需要。
The data structures in .NET that allows you to specify capacity, except for array, uses this to build the internal data structure used to hold the internal data.
.NET 中允许您指定容量的数据结构(数组除外)使用它来构建用于保存内部数据的内部数据结构。
For instance, for a list, capacity is used to size an internal array. When you start adding elements to the list, it'll start filling this array from index 0 and up, and when it reaches your capacity, it increases the capacity to a new higher capacity, and continues filling it up.
例如,对于列表,容量用于确定内部数组的大小。当您开始向列表添加元素时,它将开始从索引 0 开始填充该数组,当它达到您的容量时,它将容量增加到新的更高容量,并继续填充它。
回答by Robel.E
Well I hope this class will helps You:
Internally the Circular FIFO Buffer use a Queue<T> with the specified size.
Once the size of the buffer is reached, it will replaces older items with new ones.
好吧,我希望这门课能对您
有所帮助:在内部,循环 FIFO 缓冲区使用具有指定大小的 Queue<T>。一旦达到缓冲区的大小,它将用新项目替换旧项目。
NOTE: You can't remove items randomly. I set the method Remove(T item) to return false. if You want You can modify to remove items randomly
注意:您不能随机删除项目。我将方法 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
}
回答by CodingWithoutComments
Why wouldn't you just use an array with a size of 2? A Queue is supposed to be able to dynamically grow and shrink.
为什么不使用大小为 2 的数组?队列应该能够动态增长和收缩。
Or create a wrapper class around an instance of Queue<T>instance and each time one enqueues a <T>object, check the size of the queue. If larger than 2, dequeue the first item.
或者围绕实例的Queue<T>实例创建一个包装类,每次将一个<T>对象加入队列时,检查队列的大小。如果大于 2,则将第一项出列。
回答by SwiftArchitect
Concurrent Solution
并发解决方案
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);
}
}
}
Note: Since Enqueuecontrols the addition of elements, and does so one at a time, there is no need to execute a whilefor TryDequeue.
注意:由于Enqueue控制元素的添加,并且一次添加一个,因此无需执行whilefor TryDequeue。
回答by mpen
If it's of any use to anyone, I made a LimitedStack<T>.
如果它对任何人有用,我做了一个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; }
}
}
It removes the oldest item (bottom of stack) when it gets too big.
当它变得太大时,它会删除最旧的项目(堆栈底部)。
(This question was the top Google result for "C# limit stack size")
(这个问题是“C# 限制堆栈大小”的最高谷歌结果)
回答by Brandon
You can use a LinkedList<T>and add thread safety:
您可以使用 aLinkedList<T>并添加线程安全:
public class Buffer<T> : LinkedList<T>
{
private int capacity;
public Buffer(int capacity)
{
this.capacity = capacity;
}
public void Enqueue(T item)
{
// todo: add synchronization mechanism
if (Count == capacity) RemoveLast();
AddFirst(item);
}
public T Dequeue()
{
// todo: add synchronization mechanism
var last = Last.Value;
RemoveLast();
return last;
}
}
One thing to note is the default enumeration order will be LIFO in this example. But that can be overridden if necessary.
需要注意的一件事是,在此示例中,默认枚举顺序将是 LIFO。但如有必要,可以覆盖它。

