Queue内部通过数组实现数据的存储,字段_head和_tail分别表示队首元素和队尾元素,字段_size表示存储的元素数量,其相关字段如下:
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 //扩容系数(实际就是2)
private const int _DefaultCapacity = 4; //默认容量
static T[] _emptyArray = new T[0]; //默认空数组
Enqueue函数:
// Adds item to the tail of the queue.
//
/// <include file='doc\Queue.uex' path='docs/doc[@for="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++;
}
首先判断内部数组是否已满,若满则扩容。在这里可以看到_GrowFactor其实就是个2;_tail通过对内部数组长度取模来保证数据不会溢出出现错误。
Dequeue函数:
// Removes the object at the head of the queue and returns it. If the queue
// is empty, this method simply returns null.
/// <include file='doc\Queue.uex' path='docs/doc[@for="Queue.Dequeue"]/*' />
public T Dequeue() {
if (_size == 0)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
T removed = _array[_head];
_array[_head] = default(T);
_head = (_head + 1) % _array.Length;
_size--;
_version++;
return removed;
}
同样_head也通过取模保证数据不会溢出防止出现错误,出现数组越界的错误。
Peek函数:
// Returns the object at the head of the queue. The object remains in the
// queue. If the queue is empty, this method throws an
// InvalidOperationException.
/// <include file='doc\Queue.uex' path='docs/doc[@for="Queue.Peek"]/*' />
public T Peek() {
if (_size == 0)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
return _array[_head];
}
直接返回队首元素。
Queue的存储分布有两种情况:
1、队首在前队尾灾后,即_head < _tail;
2、队首灾后队尾在前,即_head > _tail。
_tail代表的是下一个新元素在内部数组将要存放的的索引下标,所以这里不存在_head == _tail情况的存在,在这种情况发生前内部数组会被存满,此时会进行扩容,重新new出一个新的数组进行存放数据,且把数据重定向为队首在前队尾在后的状态,扩容函数如下:
// PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity
// must be >= _size.
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++;
}
可以看到在数据拷贝的过程中进行了重定向,把队首元素重定向为下标索引为0;只要理清了这两种对首队尾的情况,Queue也就不难理解了,其余的函数就不分析了,主要还是对首队尾的情况。
源码为.net 4.5.1;
.net源码网址:https://referencesource.microsoft.com/
注:本人水平有限,如果有错误,欢迎指正……