While reviewing the data structure, I used ilspy to see some. NET class library implementation, found some basic data structure implementation method is also very interesting, so here to share with you. This article discusses the generic implementations of stack and queue.
The realization of stack<t>
Stack (stack) is a last-in-first-out data structure, where the core two methods are push (stack) and pop (out of stack) two operations, then. NET class library is how to implement this data structure? To reduce the cost of learning, this will be based on. NET source of the implementation, combined with the core design ideas, to obtain a simplified version of the implementation:
usingSystem;namespaceoriginalcode{/// <summary> ///based on. NET source of the simplified version of the implementation/// </summary> Public classStack<t> { Private Const int_defaultcapacity =4; Privatet[] _array; Private int_size; PublicStack () {//the default number of initialized arrays is empty_array =Newt[0]; //the number of initialized arrays is 0_size =0; } /// <summary> ///into the stack/// </summary> /// <param name= "Item" >elements into the stack</param> Public voidPush (T item) {if(_size = =_array. Length) {//array storage is full and the array size needs to be reassigned//The allocated array size is twice times the originalt[] Array =NewT[_array. Length = =0? _defaultcapacity:2*_array. Length]; //copy the original array into the new arrayCopy (_array, array); //_array pointing to the new array_array =Array; } _array[_size]=item; _size+=1; } /// <summary> ///out of the stack/// </summary> /// <returns>the elements of the stack</returns> PublicT Pop () {if(_size = =0) { Throw NewException ("The stack is empty and the stack operation cannot be performed at the moment"); } _size-=1; T result=_array[_size]; _array[_size]=default(T); returnresult; } /// <summary> ///assigning an old array to a new array (this method is a simulation implementation, the actual situation.) NET source code to achieve more efficient replication with C + +)/// </summary> /// <param name= "Oldarray" >Old Array</param> /// <param name= "NewArray" >new Array</param> Private voidCopy (t[] Oldarray, t[] newarray) { for(inti =0; i < oldarray.length; i++) {Newarray[i]=Oldarray[i]; } } }}
implementation of the simplified version of the stack
It must be made clear that the bottom of the stack<t> is maintained by the t[] _array array object. First of all, the constructor stack (), what is done here is nothing more than some basic initialization work, when calling this parameterless constructor, the _array array is instantiated as t[0], and a _size is initialized to 0. This _size is mainly used to represent the number of elements present in the stack, and also to assume the role of an array subscript, identifying the array position of the next element in the stack.
Next, consider the implementation of the push (T item) function. The first step here is to execute a judgment, determine whether the current _array array of elements is full, if full, it will expand the array. NET source for the array expansion of the design is still relatively clever, when the _array is empty, the default beginning to allocate the number of arrays is 4, both new t[4], if you want to insert the 5th element, when the number of arrays is insufficient, declare a new t[] array, and the number is expanded to _ Array expansion is done by copying the _array element to the new array, and finally pointing the _array field to the array, which is twice times the number of arrays. This step in the previous code implementation should be very clear, but one thing to note is that the copy (_array,array) function here is a simple implementation of my own, and the implementation of the. NET source code is very different,. NET source code is to invoke a array.copy (this._array, 0, array, 0, this._size) functions, its underlying should be implemented in C + + to achieve better optimization of array replication. Let's look at the process of array expansion by a single graph:
Finally, consider the implementation of the POP () function. First determine whether the number of current array is greater than 0, less than or equal to 0 will throw an exception. After that, you will _size-=1 the position of the object to be popped in the array. When you remove _array[_size], the position of the default (T) fill _array[_size] is called, and one of the benefits of this is that it cancels the reference to the original object and is the object that it can be garbage collected to better reduce the memory footprint. Generally speaking, pop () implementations are relatively simple.
From the previous we know that using stack<t> data structures, array expansion should be one of the factors that affect performance the most. By default, if you want to insert 100 objects into the stack, it means that the array is going through the 4->8->16->32->64->128 total of 5 arrays, so is there any way to improve performance? The answer is yes. NET source Stack<t> object provides a constructor for a stack (int capacity) In addition to the default parameterless constructor, and the capacity parameter is actually the number of arrays initialized with a representation, If we can anticipate the maximum number of objects in the insert stack (in 100, for example), call new Stack<t> (100) directly so that you can reduce unnecessary array expansion, thus improving the performance of the stack.
The realization of queue<t>
A queue is a first-in-a-enqueue data structure, with the most core of the two methods being the Dequeue (enqueue) and the two operations (out of the team). Through the previous warm-up, we have already understood the implementation of the stack<t>, in fact, the implementation of queue<t> also have similar places, such as the underlying data structure is also dependent on t[] _array array objects to maintain, but also used twice times the way array expansion. However, because the queue has a FIFO feature, it determines that you cannot use a _size like stack<t> to maintain the bottom of the stack tail, the queue must have a team head _head subscript and a tail _tail subscript to ensure the first-out feature. Considering the storage efficiency of the queue, there must also be a problem with circular queues, so the implementation of queue<t> is more complex than the stack<t>, and it also looks at a simplified version of the implementation:
usingSystem;namespaceoriginalcode{/// <summary> ///based on. NET source of the simplified version of the implementation/// </summary> Public classQueue<t> { Private Statict[] Empty_array =Newt[0]; Private Const int_defaultcapacity =4; Privatet[] _array; Private int_head;//Head Position Private int_tail;//Tail Position Private int_size;//number of queue elements PublicQueue () {_array=Empty_array; _head=0; _tail=0; _size=0; } PublicQueue (intcapacity) {_array=NewT[capacity]; _head=0; _tail=0; _size=0; } /// <summary> ///Queued Operation/// </summary> /// <param name= "Item" >elements to be queued</param> Public voidEnqueue (T item) {if(_size = =_array. Length) {//determine the size of the expanded capacity intCapacity = _array. Length *2; if(Capacity < _array. Length +_defaultcapacity) { //. NET source code to realize some basic conjecture//because the queue can be instantiated by calling the queues (int capacity) capacity can =1 | 2 | 3//here to do with +4 to make a judgment should be to improve the basic performance such as when capacity = 1 when * * = 2 so 2 quickly easy to have the next expansion//But in fact, the effect is not a little too much design suspicionCapacity = _array. Length +_defaultcapacity; } //instantiate an array with a larger capacityt[] Array =NewT[capacity]; if(_size >0) { //when the array memory needs to be reassigned according to the characteristics of the loop queue at this time the _head must be equal to _tail//copy from old array _array[_head] to _array[_size-1] to new array array[0] ... [_size-_head-1]ArrayCopy (_array, Array,0, _head, _size-_head); //copy from old array _array[0] to _array[_head-1] to new array array[_size-_head] ... [_size-1]ArrayCopy (_array, array, _size-_head,0, _head); } _array= array;//point old array to new array_head =0;//Reset the head position to 0_tail = _size;//reset the tail position to _size} _array[_tail]=item; _tail= (_tail +1) %_array. Length; _size+=1; } /// <summary> ///out of team Operation/// </summary> /// <returns>out of the team element</returns> PublicT Dequeue () {if(_size = =0) { Throw NewException ("the current queue is empty and cannot perform an out of band Operation"); } T result=_array[_head]; _array[_head]=default(T); _head= (_head +1) %_array. Length; _size-=1; returnresult; } /// <summary> ///copies the items of the old array to the new array (this method is a simulation implementation, the actual situation.) NET source code to achieve more efficient replication with C + +)/// </summary> /// <param name= "Oldarray" >Old Array</param> /// <param name= "NewArray" >new Array</param> /// <param name= "Newarraybeginindex" >new Array Start item subscript</param> /// <param name= "Oldarraybeginindex" >old array start subscript</param> /// <param name= "Copycount" >Number of copies</param> Private voidArrayCopy (t[] Oldarray, t[] NewArray,intNewarraybeginindex,intOldarraybeginindex,intCopycount) { for(inti = oldarraybeginindex, j = newarraybeginindex; I < Oldarraybeginindex + Copycount; i++,j++) {Newarray[j]=Oldarray[i]; } } }}
implementation of the simplified version of the queue
Let's start by looking at the following diagram to see if the array capacity is sufficient, the execution of the loop queue:
Based on the above diagram, take a look at the implementation of the Dequeue function. The first step is to determine whether _size is 0, and then throws an exception. If the current queue number is greater than 0, get the _array[_head] element as the outbound element, and then call Default (T) to fill the position of _array[_head]. Due to the design of a cyclic queue, it is not possible to simply _head+=1, and this must be _head= (_head+1)%_array. Length, as shown, _head may point to the position of the subscript 3, if at this point directly _head + = 1 becomes 4, it jumps out of the array of small range, and _head= (_head+1)%_array. Length becomes 0, which points to the first position of the array, realizes the function of the loop queue, and uses the memory better.
Next, take a look at the implementation of the Enqueue (T item) function. Accept the status of the queue, assuming now to execute Q. Enqueue ("F") of the queue operation, but it is clear that the array _array is full, then what to do? In fact, the principle and the implementation of the stack is similar, but also through the expansion of the array way, but more complex than stack copy of the array. To continue looking at the picture:
As with stack<t>, the biggest factor affecting queue<t> performance is array expansion and the corresponding array copy operation, and the queue also provides a constructor with the initialized capacity queue (int capacity). If we can estimate that the queue may have the maximum value of the element at the same time, try to invoke this capacity constructor.
. NET source stack<t> and queue<t> implementation