堆栈(Stack)代表了一个后进先出的对象集合。当需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当从列表中移除一项时,称为弹出元素。
Stack<T>
属性
Count:获取 Stack 中包含的元素个数。
方法 | 描述 |
---|---|
void Clear(); | 移除所有的元素 |
bool Contains( object obj ); | 判断某个元素是否存在 |
object Peek(); | 返回顶部对象,但不移除它 |
object Pop(); | 移除并返回顶部对象 |
void Push( object obj ); | 向顶部添加一个对象 |
object[] ToArray(); | 复制到一个新的数组中 |
C#Stack底层原理
构造函数
public Stack() {
_array = new Object[_defaultCapacity];
_size = 0;
_version = 0;
}
// Create a stack with a specific initial capacity. The initial capacity
// must be a non-negative number.
public Stack(int initialCapacity) {
if (initialCapacity < 0)
throw new ArgumentOutOfRangeException("initialCapacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
Contract.EndContractBlock();
if (initialCapacity < _defaultCapacity)
initialCapacity = _defaultCapacity; // Simplify doubling logic in Push.
_array = new Object[initialCapacity];
_size = 0;
_version = 0;
}
创建Stack构造函数时,不添加容量。默认会把容量设置为10.
1、Clear()函数
遗憾的是,我搜索了Array.Clear到底是通过什么方式去清楚这些元素。但是没有搜到。微软的文档里没有写。如果有知道的朋友请留言。java的Array.Clear说是通过for循环将对应元素赋值为null
2、bool Contains( object obj );函数
通过while遍历的形式来找到元素,时间复杂度O(n)。从这段代码我们可以看出Stack是允许插入null值的,翻回到前面的Push(),我们也可以看到,插入的时候并没有对数据做任何的约束。
3、object Peek(); 返回顶部对象,但不移除它
4、object Pop();移除并返回顶部对象
public virtual Object Pop() { if (_size == 0) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack")); //Contract.Ensures(Count == Contract.OldValue(Count) - 1); Contract.EndContractBlock(); _version++; Object obj = _array[--_size]; _array[_size] = null; // Free memory quicker. return obj; }
Peek和Pop的差别在于,Peek只是返回栈顶,Pop是返回栈顶元素的同时并将其删除,所以Peek实质并没有对数据进行更改,不会增加版本号,可以在foreach中使用,而Pop对数据做出了修改,会导致_Version(版本号)+1。_version用来记录版本号(用来在foreach的时候进行判断,任何对栈的更改都会使版本号+1,如果循环中版本号有变化,会抛出异常)。
5、void Push( object obj );向顶部添加一个对象
public virtual void Push(Object obj) { //Contract.Ensures(Count == Contract.OldValue(Count) + 1); if (_size == _array.Length) { Object[] newArray = new Object[2*_array.Length]; Array.Copy(_array, 0, newArray, 0, _size); _array = newArray; } _array[_size++] = obj; _version++; }
当栈的容量已经满了时,创建一个新的数组容器newArray。newArray的容量时初始容量的两倍。将_array中的数据复制给newArray。最后将newArray当作栈的数组容量,_array=newArray;接着讲值放置在数组的末尾,版本号+1(因为栈的数据改变了)。
6、object[] ToArray();
public virtual Object[] ToArray() { Contract.Ensures(Contract.Result<Object[]>() != null); Object[] objArray = new Object[_size]; int i = 0; while(i < _size) { objArray[i] = _array[_size-i-1]; i++; } return objArray; }
遍历所有的元素,进行到新建的数组中,唯一注意的一点是,得到的数组和Stack中的数组顺序是相反的,也就是说栈顶的元素放在了返回的数组的头部。