C Queue原始碼剖析

2021-07-11 13:26:29 字數 4001 閱讀 6087

源**版本為 .net framework 4.6.1

有投入,有產出。

(注:非基礎性,主要涉及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倍於原容量的陣列)。

基本成員

private t _array;

private

int _head; // 表頭

private

int _tail; // 表尾

private

int _size; // 佇列元素數量

private

int _version;

[nonserialized]

private object _syncroot;

private

const

int _minimumgrow = 4; // 最小增長值

private

const

int _shrinkthreshold = 32; // 收縮閾值

private

const

int _growfactor = 200; // 每次增長雙倍

private

const

int _defaultcapacity = 4; // 預設容量

static t _emptyarray = new t[0]; //空陣列

_head:乙個指向佇列頭元素的索引,因為是迴圈列隊,_head值有可能大於_tail值。

_tail:乙個指向佇列尾元素的索引,因為是迴圈列隊,_tail值有可能笑於_head值。

_size:記錄佇列中元素的總數量。

_minimumgrow: 佇列每一次的最小擴容量。

_shrinkthreshold:「迷」之收縮閾值。

_growfactor:增長因子。

_defaultcapacity: 預設初始容量。

_emptyarray:預設空陣列。

初始化函式

public

queue()

public

queue(int capacity)

public

queue(ienumerablecollection)

}

}

有三個初始化函式,一般初始化都會使用初始預設值_head和_tail最初都指向索引0(不代表_head=_tail就是空陣列),_size=0長度為0。

入隊

突然想起,軍訓期間被罰做完俯臥撐後,教官大喊入隊。

入隊的方法:enqueue(t item)

public

void

enqueue(t item)

setcapacity(newcapacity);

}_array[_tail] = item;

_tail = (_tail + 1) % _array.length;

_size++;

_version++;

}

入隊看起來很簡單,通過_tail拿到隊尾索引,將元素插入隊尾即可。但是這裡有一點小細節:

_tail = (_tail + 1) % _array.length
下個元素的隊尾索引 = (當前隊尾索引 + 1) % 資料陣列長度,這裡的資料陣列長度不等同與佇列元素數量。此公式與抑制了隊尾索引不會超過資料陣列長度,從而避免了資料溢位的產生(同時也會導致隊尾索引會小於隊頭索引,需要分情況進行處理)。

出隊

出隊有兩種方法

(1)public t peek():返回位於 queue 開始處的物件但不將其移除。

(2)public t dequeue():移除並返回位於 queue 開始處的物件。

public t peek() 

public t dequeue()

peek()簡單粗暴,通過索引直接返回資料。

queue沒有提供remove方法,但是dequeue具有刪除功能並返回頭元素。被移除的元素直接指向null(空引用)。並且頭元素索引向前移動(這是乙個跑圈圈的遊戲,***)。

查詢

contains(t item)判斷佇列中是否至少包含乙個匹配的元素存在 是則返回true,否則返回false。

public bool contains(t item)  

else

if (_array[index] != null && c.equals(_array[index], item))

index = (index + 1) % _array.length;

}return

false;

}

容量調整容量調整,可以重置佇列空間,如果元素數小於當前容量的 90%,將容量設定為 queue中的實際元素數。

public

void

trimexcess()

}

private

void

setcapacity(int capacity) else

}_array = newarray;

_head = 0;

_tail = (_size == capacity) ? 0 : _size;

_version++;

}

最後(1)queue可以通過trimexcess()方法,將容量下降到實際元素的數量.

(2)queue允許重複的元素。

(3)queue和stack主要是用來儲存臨時資訊的。(下篇文章寫stack,一種後進先出的集合)

原始碼剖析 Hashtable 原始碼剖析

hashtable同樣是基於雜湊表實現的,同樣每個元素都是key value對,其內部也是通過單鏈表解決衝突問題,容量不足 超過了閾值 時,同樣會自動增長。hashtable也是jdk1.0引入的類,是執行緒安全的,能用於多執行緒環境中。hashtable同樣實現了serializable介面,它支...

python原始碼剖析 Python原始碼剖析

第頁共 頁python 原始碼剖析 物件機制 1.物件 在python 的世界中,一切都是物件,乙個整數是乙個物件,乙個字串也是 乙個物件,更為奇妙的是,型別也是乙個物件,整數型別是乙個物件,字串類 型也是乙個物件。從 年guido 在那個聖誕節揭開 python 世界的大幕開始,一直到現在,pyt...

Erlang hotwheels原始碼剖析

整體構架 janus transport sup 實質為transport,supervisor,client instance supervisor 每個tcp會話建立乙個transport程序來處理對應客戶端的請求。janus topman sup 實質為topman,worker,topic ...