Queue<T>是c#的泛型队列类,跟Stack、List等容器一样,它的内部也是由数组来实现的,主要为使用者提供了Enqueue()、Peek()、Dequeue()、Contains()、GetElement()等接口来进行使用。下面我们会逐个来进行分析,先来看下Queue的类头:
public class Queue<T> : IEnumerable<T>,
System.Collections.ICollection,
IReadOnlyCollection<T> {
private T[] _array;
private int _head; // First valid element in the queue
private int _tail; // Last valid element in the queue
private int _size; // Number of elements.
private int _version;
#if !SILVERLIGHT
[NonSerialized]
#endif
private Object _syncRoot;
private const int _MinimumGrow = 4;
private const int _ShrinkThreshold = 32;
private const int _GrowFactor = 200; // double each time
private const int _DefaultCapacity = 4;
static T[] _emptyArray = new T[0];
// Creates a queue with room for capacity objects. The default initial
// capacity and grow factor are used.
/// <include file='doc\Queue.uex' path='docs/doc[@for="Queue.Queue"]/*' />
public Queue() {
_array = _emptyArray;
}
// Creates a queue with room for capacity objects. The default grow factor
// is used.
//
/// <include file='doc\Queue.uex' path='docs/doc[@for="Queue.Queue1"]/*' />
public Queue(int capacity) {
if (capacity < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired);
_array = new T[capacity];
_head = 0;
_tail = 0;
_size = 0;
}
可以看到定义了一个泛型数组_array,_head为队列头部在数组中的索引 ,_tail为尾部索引看, _size为队列大小,
_version用来记录版本号(用来在foreach的时候进行判断,任何对栈的更改都会使版本号+1,如果循环中版本号有变化,会抛出异常)。_DefaultCapacity为默认在第一次为栈添加元素时创建的数组大小。Queue默认的无参构造函数会将数组_array指向静态数组_emptyArray,也可以在创建Queue时指定栈数组的初始大小,这样Queue会直接创建相应大小的数组元素(这样的好处是:在已知队列大小的情况下,指定队列数组的大小,可以减少数组空间不够时,重新创建新的数组和拷贝的消耗)。
接下来我们先来看看Enqueue函数:
public void Enqueue(T item) {
if (_size == _array.Length) {
int newcapacity = (int)((long)_array.Length * (long)_GrowFactor / 100);
if (newcapacity < _array.Length + _MinimumGrow) {
newcapacity = _array.Length + _MinimumGrow;
}
SetCapacity(newcapacity);
}
_array[_tail] = item;
_tail = (_tail + 1) % _array.Length;
_size++;
_version++;
}
private void SetCapacity(int capacity) {
T[] newarray = new T[capacity];
if (_size > 0) {
if (_head < _tail) {
Array.Copy(_array, _head, newarray, 0, _size);
} else {
Array.Copy(_array, _head, newarray, 0, _array.Length - _head);
Array.Copy(_array, 0, newarray, _array.Length - _head, _tail);
}
}
_array = newarray;
_head = 0;
_tail = (_size == capacity) ? 0 : _size;
_version++;
}
添加到队列末时会先进行判断,如果当前队列已满则分配两倍大小新内存块,_GrowFactor值为200(感觉有点蠢,跟List<T>一样直接*2不就完了吗?而且计算结束后还得判断是否小于最小值,因为如果第一次的话0*2=0,其实完全可以这样写:int newcapacity = _size == 0 ? _DefaultCapacity : _size * 2;)。接着把item放入_array的“尾”部,尾部索引的值等于自增再对_array长度进行取余,并对_size,_version自增。
SetCapacity函数重新申请分配了capacity大小的内存,并对原有数据进行了拷贝。
看到这里你可能会有点疑惑,_tai