Реализация стека в 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, вы можете увидеть содержимое фактического внутреннего массива, используемого для хранения элементов, и проверить, что они находятся в ожидаемом порядке.


Я не вижу, какая разница, какой конец они считают верхней частью стека, пока вы теперь знаете, какой это конец. На самом деле имеет смысл, что когда вы "нажимаете" что-то на стек, вы нажимаете его сверху (начало) и перемещаете другие элементы вниз...