c# Queue源码解析

        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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值