標籤:
原始碼版本為 .NET Framework 4.6.1
本系列持續更新,敬請關注
有投入,有產出。
(註:非基礎性,主要涉及Stack的實現原理)
水平有限,若有不對之處,望指正。
Stack(棧)表示對象的後進先出 (LIFO) 集合。實現了ICollection介面。
概念
定義:限定僅在表尾進行插入或刪除操作的線性表,表尾對應棧頂,表頭對應棧底,不含元素的棧稱為空白棧。
入棧:往棧頂插入一個元素。
出棧:在棧頂刪除一個元素
元素的操作只能在棧頂進行,最後入棧的元素最先出棧,結構圖如下:
進入主題
基本成員
private T[] _array; // 用於儲存棧元素的數組 private int _size; // 棧元素的數量 private const int _defaultCapacity = 4; // 預設初始容量 static T[] _emptyArray = new T[0]; // 空數組,用於賦預設值
建構函式
Stack有三個建構函式,分別為:
(1)全部使用Stack設定的預設值,不推薦使用(原因是動態擴容需要額外的計算與開闢新的記憶體空間,動態擴容應該發生在超出預期容量值範圍的情況下,抑制溢出);
public Stack() { _array = _emptyArray; _size = 0; _version = 0; }
(2)使用一個非負的整數設定Stack的一個初始容量,推薦使用(給定一個預期的容量值,若預期值小了,會自動擴容,也不擔心溢出)
public Stack(int capacity) { if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired); _array = new T[capacity]; _size = 0; _version = 0; }
(3)使用一個現有的非Null 參考集合進行填充初始化Stack,Stack具有與該集合相同的長度,並且按照集合元素的儲存順序(推薦使用實現了Collection泛型介面的集合,可以獲得一些效能上的提升)。
public Stack(IEnumerable<T> collection) { if (collection==null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); ICollection<T> c = collection as ICollection<T>; if( c != null) { //實現了ICollection泛型介面的集合,簡單粗暴,分配空間,數組複製 int count = c.Count; _array = new T[count]; c.CopyTo(_array, 0); _size = count; } else { _size = 0; //沒有實現ICollection泛型介面的,預設初始容量為4,空間不夠還需動態擴容 _array = new T[_defaultCapacity]; //遍曆元素 執行入棧操作 using(IEnumerator<T> en = collection.GetEnumerator()) { while(en.MoveNext()) { Push(en.Current); } } } }
入棧
入棧是Stack最核心的方法之一,將元素從棧頂插入到Stack中。觸發動態擴容的條件若且唯若元素數量等於內部儲存元素的數組長度時,開闢一個新的記憶體空間,原來的2倍,原來的記憶體空間將被GC回收並釋放資源
public void Push(T item) { //動態擴容 if (_size == _array.Length) { T[] newArray = new T[(_array.Length == 0) ? _defaultCapacity : 2*_array.Length]; Array.Copy(_array, 0, newArray, 0, _size); _array = newArray; } //將元素存於棧頂位置 _array[_size++] = item; _version++; }
出棧
出棧是Stack最核心的方法之一,擷取位於棧頂的元素,空棧不具備出棧功能(強制使用將會引發異常)。分為兩種:
(1)只擷取棧頂元素,不移除。
public T Peek() { if (_size==0) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack); return _array[_size-1];//擷取棧頂元素 }
(2)擷取棧頂元素並將其移除。
public T Pop() { if (_size == 0) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack); _version++; T item = _array[--_size]; // 擷取棧頂元素,同時棧頂指標前移一位 _array[_size] = default(T); // 釋放棧頂的記憶體空間 return item; }
空間壓縮
如果實際元素數量小於當前容量的 90%,將容量設定為 Stack中的實際元素數。推薦在確定不會再有太多的入棧元素時並且初始化容量相比預期值大得多時使用。
public void TrimExcess() { int threshold = (int)(((double)_array.Length) * 0.9); if( _size < threshold ) { T[] newarray = new T[_size]; Array.Copy(_array, 0, newarray, 0, _size); _array = newarray; _version++; } }
遍曆
Stack實現了IEnumerable泛型介面的GetEnumerator(),返回一個結構體:
public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator{ private Stack<T> _stack; //遍曆的當前棧 private int _index; // 遍曆標識 private int _version; private T currentElement; //當前棧元素 //初始化方法 internal Enumerator(Stack<T> stack) { _stack = stack; _version = _stack._version; _index = -2; currentElement = default(T); } //指標移動,遍曆必須的方法 public bool MoveNext() { bool retval; if (_version != _stack._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); if (_index == -2) { // 開始遍曆 _index = _stack._size-1; retval = ( _index >= 0); if (retval) currentElement = _stack._array[_index]; return retval; } if (_index == -1) { // 結束遍曆 return false; } retval = (--_index >= 0); if (retval) currentElement = _stack._array[_index]; else currentElement = default(T); return retval; } //擷取遍曆的當前元素,與MoveNext結合使用,即遍曆必須的屬性 public T Current { get { if (_index == -2) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); if (_index == -1) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); return currentElement; } } }
C# Stack源碼剖析