1. 程式人生 > 實用技巧 >Core原始碼(十一)Queue

Core原始碼(十一)Queue

Queue表示物件的先進先出集合。實現了ICollection介面,可以由陣列或連結串列兩種形式實現,在.NET中是以陣列的形式實現的。

概念

佇列是一種特殊的線性表,特殊之處在於它只允許在表頭(head)進行刪除操作,而在表尾(tail)進行插入操作。

  佇列的資料元素又稱為佇列元素。在佇列中插入一個佇列元素稱為入隊,從佇列中刪除一個佇列元素成為出隊。因為佇列只允許在一段插入,在另一端刪除,所以只有最早進入佇列的元素才能最先從佇列中刪除,故佇列又稱為先進先出(FIFO—first in first out)線性表

佇列可以分為順序佇列和迴圈佇列,.NET中為了提高空間的利用率,採用的是迴圈佇列。

迴圈佇列

為充分利用向量空間,克服”假溢位”(由於入隊和出隊操作中,頭尾指標只增加不減小,致使被刪元素的空間永遠無法重新利用)現象的方法是:將向量空間想象為一個首尾相接的圓環,並稱這種向量為迴圈向量。儲存在其中的佇列稱為迴圈佇列(Circular Queue)。概念圖如下:

  迴圈佇列中,由於入隊時尾指標向前追趕頭指標;出隊時頭指標向前追趕尾指標,造成空佇列和滿佇列時頭尾指標均相等。因此,無法通過條件front==rear來判別佇列是”空”還是”滿”,.NET使用一下方法判斷空佇列和滿佇列(實際.NET中,佇列的長度時自動擴容的):

(1)私有成員_size = 0時,為空佇列。

(2)_size == _array.Length時(_array為Queue內部維護的實際資料陣列),為滿佇列,這個時候會進行自動擴容(新建一個2倍於原容量的陣列)。

  而出隊入隊的位置,也要用取模的方式計算,因為可能出現數組尾部到頭部的迴圈,如下

_head = (_head + 1) % _array.Length;

屬性和變數

/// <summary>
/// 內部維護的陣列,實際進行資料的存放
/// </summary>
private Object[] _array;

/// <summary>
/// First valid element in the MyQueue  表頭
/// </summary> private int _head; /// <summary> /// Last valid element in the MyQueue 表尾 /// </summary> private int _tail; /// <summary> /// Number of elements. 佇列元素數量 /// </summary> private int _size; public virtual int Count { get { return _size; } } private int _growFactor; // 100 == 1.0, 130 == 1.3, 200 == 2.0 private int _version; [NonSerialized] private Object _syncRoot; private const int _MinimumGrow = 4; private const int _ShrinkThreshold = 32;

建構函式

public MyQueue(int capacity, float growFactor)
{
    if (capacity < 0)
        throw new ArgumentOutOfRangeException();
    if (!(growFactor >= 1.0 && growFactor <= 10.0))
        throw new ArgumentOutOfRangeException();

    _array = new Object[capacity];
    _head = 0;
    _tail = 0;
    _size = 0;
    _growFactor = (int)(growFactor * 100);
}

新增及擴容

入隊
// Adds obj to the tail of the MyQueue.
public virtual void EnMyQueue(Object obj)
{
    //這時候需要擴容
    if (_size == _array.Length)
    {
        //使用_growFactor計算新的容量
        int newcapacity = (int)((long)_array.Length * (long)_growFactor / 100);
        if (newcapacity < _array.Length + _MinimumGrow)
        {
            newcapacity = _array.Length + _MinimumGrow;
        }
        SetCapacity(newcapacity);
    }
    //尾部指標位置賦值
    _array[_tail] = obj;
    //迴圈佇列,要進行餘數運算
    _tail = (_tail + 1) % _array.Length;
    _size++;
    _version++;
}

//設定佇列的容量
private void SetCapacity(int capacity)
{
    Object[] newarray = new Object[capacity];
    if (_size > 0)
    {
        if (_head < _tail) //正序的情況直接複製
        {
            Array.Copy(_array, _head, newarray, 0, _size);
        }
        else
        {
            //先把頭部到陣列結束的資料複製
            Array.Copy(_array, _head, newarray, 0, _array.Length - _head);
            //再複製0到尾部的資料
            Array.Copy(_array, 0, newarray, _array.Length - _head, _tail);
        }
    }
    _array = newarray;
    _head = 0;
    //_size == capacity尾部設定為0,這樣下次新增的時候是需要擴容的
    _tail = (_size == capacity) ? 0 : _size;
    _version++;
}

出隊和清空

// Removes the object at the head of the MyQueue and returns it. If the MyQueue
// is empty, this method simply returns null.
public virtual Object DeMyQueue()
{
    if (Count == 0)
        throw new InvalidOperationException();
    //返回出隊物件
    Object removed = _array[_head];
    //指向設定為空
    _array[_head] = null;

    //重新獲取新的佇列頭部位置
    _head = (_head + 1) % _array.Length;
    _size--;
    _version++;
    return removed;
}

// Removes all Objects from the MyQueue.
public virtual void Clear()
{
    if (_head < _tail)
        Array.Clear(_array, _head, _size);
    else
    {
        Array.Clear(_array, _head, _array.Length - _head);
        Array.Clear(_array, 0, _tail);
    }

    _head = 0;
    _tail = 0;
    _size = 0;
    _version++;
}

查詢

public virtual bool Contains(Object obj)
{
    //從同步開始遍歷,遍歷次數為_size
    int index = _head;
    int count = _size;
    while (count-- > 0)
    {
        if (obj == null)
        {
            if (_array[index] == null)
                return true;
        }
        else if (_array[index] != null && _array[index].Equals(obj))
        {
            return true;
        }
        //求模方式獲取下個位置
        index = (index + 1) % _array.Length;
    }
    return false;
}

internal Object GetElement(int i)
{
    return _array[(_head + i) % _array.Length];
}