Реализация стека в C#
недавно я реализовал рекурсивную реализацию поиска каталогов, и я использую стек для отслеживания элементов пути. Когда я использовал string.Join () чтобы присоединиться к элементам пути, я обнаружил, что они были обращены. Когда я отладил метод, я заглянул в стек и обнаружил, что сами элементы были обращены во внутреннем массиве стека, т. е. последний элемент Push () ed был в начале внутреннего массива, а последний элемент Push () ed был в конце внутреннего массива. Это кажется отсталым и очень противоинтуитивным. Может кто-нибудь сказать мне, почему Microsoft будет реализовывать стек таким образом ?
6 ответов
Я думаю, вы ошибаетесь.
дело не в этом Stack<T>.Push
внутренне вставляет элемент в начале своего внутреннего массива (это не так). Скорее, он перечисляет сверху вниз, так как это способ, которым можно было бы интуитивно перечислить через стек (подумайте о стопке блинов: вы начинаете сверху и работаете вниз).
если вы посмотрите на содержимое коллекции из отладчика Visual Studio, я думаю, что он отобразит их вам в порядок, в котором они перечислены, а не порядок, в котором они хранятся внутри*.
посмотри Stack<T>.Push
метод отражатель и вы увидите, что код по сути именно то, что вы ожидаете:
// (code to check array size)
this._array[this._size++] = item;
// (code to update internal version number)
таким образом, стек внутренне добавляет новые элементы в конец своего внутреннего массива. Это Stack<T>.Enumerator
класс, который вас смутил, а не Stack<T>
сам класс.
*Я не знаю, верно ли это вообще, но это верно для Stack<T>
; см. отличный ответ Ханса Пассанта по той причине, почему.
вы заставили меня пойти туда немного,это действительно выглядит полностью басом. Однако происходит что-то еще. Класс Stack имеет визуализатор отладчика с именем System_StackDebugView. Это внутренний класс, вам нужно посмотреть с рефлектором, чтобы увидеть его.
этот визуализатор имеет свойство Items, это то, на что вы смотрите при развертывании узла в отладчике. Это свойство Items использует Stack.Метод toArray(). Который выглядит как это:
public T[] ToArray()
{
T[] localArray = new T[this._size];
for (int i = 0; i < this._size; i++)
{
localArray[i] = this._array[(this._size - i) - 1];
}
return localArray;
}
Ага, назад.
то, что вы описали is правильная реализация, так как стек является структурой LIFO (последней в первом выходе). Представьте себе, что это стопка пластин,последний элемент, помещенный в стопку, является первым удаленным. Вы столкнулись со стеком в другом месте, который является FIFO?
FIFO будет очередью.
вот как реализуются методы push и pops стека. Обратите внимание, что он использует последний индекс в массиве, а не первый. Так что должна быть какая-то другая проблема, чтобы вернуть тебя назад.
public virtual void Push(object obj)
{
if (this._size == this._array.Length)
{
object[] destinationArray = new object[2 * this._array.Length];
Array.Copy(this._array, 0, destinationArray, 0, this._size);
this._array = destinationArray;
}
this._array[this._size++] = obj;
this._version++;
}
public virtual object Pop()
{
if (this._size == 0)
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack"));
}
this._version++;
object obj2 = this._array[--this._size];
this._array[this._size] = null;
return obj2;
}
чтобы добавить к другим ответам, Если в отладчике вы прокрутите вниз до нижней части элементов стекаи откроете необработанный вид - > непубличные члены - >_array, вы можете увидеть содержимое фактического внутреннего массива, используемого для хранения элементов, и проверить, что они находятся в ожидаемом порядке.
Я не вижу, какая разница, какой конец они считают верхней частью стека, пока вы теперь знаете, какой это конец. На самом деле имеет смысл, что когда вы "нажимаете" что-то на стек, вы нажимаете его сверху (начало) и перемещаете другие элементы вниз...