C#源码分析—Queue

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/

注:本人水平有限,如果有错误,欢迎指正……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值