c# клонирование стека

У меня есть несколько стеков в моем коде, которые я использую для отслеживания моего логического местоположения. В определенные моменты мне нужно дублировать стеки, но я не могу клонировать их таким образом, чтобы сохранить порядок. Мне нужно только мелкое дублирование (ссылки, а не объект).

каким будет правильный способ сделать это? Или я должен использовать какой-то другой вид стеков?

примечание: Я видел этот пост проблема клонирования стека: ошибка .NET или ожидаемое поведение?, но не знаете как настройка метода клонирования для класса Stack.

примечание #2: я использую систему.Коллекция.Родовой.Стек

4 ответов


var clonedStack = new Stack<T>(new Stack<T>(oldStack));

вы можете написать это как метод расширения как

public static Stack<T> Clone<T>(this Stack<T> stack) {
    Contract.Requires(stack != null);
    return new Stack<T>(new Stack<T>(stack));
}

это необходимо, потому что Stack<T> конструктор, который мы используем здесь Stack<T>(IEnumerable<T> source) и конечно, когда вы перебирайте IEnumerable<T> реализация Stack<T> он будет неоднократно поп-элементы из стека, таким образом, подавая их вам в обратном порядке, что вы хотите, чтобы они пошли на новый стек. Поэтому выполнение этого процесса дважды приведет к тому, что стек будет находиться в правильный порядок.

кроме того:

var clonedStack = new Stack<T>(oldStack.Reverse());


public static Stack<T> Clone<T>(this Stack<T> stack) {
    Contract.Requires(stack != null);
    return new Stack<T>(stack.Reverse());
}

опять же, мы должны идти в стек в обратном порядке, от выхода из итерации в стек.

Я сомневаюсь, что есть разница в производительности между этими двумя методами.


вот один простой способ, используя LINQ:

var clone = new Stack<ElementType>(originalStack.Reverse());

вы можете создать метод расширения, чтобы сделать это проще:

public static class StackExtensions
{
    public static Stack<T> Clone<T>(this Stack<T> source)
    {
        return new Stack<T>(originalStack.Reverse());
    }
}

использование:

var clone = originalStack.Clone();

для тех, кто заботится о производительности.. Есть несколько других способов, как перебирать исходные члены стека без больших потерь в производительности:

public T[] Stack<T>.ToArray();
public void Stack<T>.CopyTo(T[] array, int arrayIndex);

Я написал грубую программу (ссылка будет предоставлена в конце поста) для измерения производительности и добавил два теста для уже предложенных реализаций (см. Clone1 и Clone2), и два теста для метод toArray и копировать, чтобы подходы (см. Clone3 и Clone4, оба из них используют более эффективным!--5-->массив.Обратный метод).

public static class StackExtensions
{
    public static Stack<T> Clone1<T>(this Stack<T> original)
    {
        return new Stack<T>(new Stack<T>(original));
    }

    public static Stack<T> Clone2<T>(this Stack<T> original)
    {
        return new Stack<T>(original.Reverse());
    }

    public static Stack<T> Clone3<T>(this Stack<T> original)
    {
        var arr = original.ToArray();
        Array.Reverse(arr);
        return new Stack<T>(arr);
    }

    public static Stack<T> Clone4<T>(this Stack<T> original)
    {
        var arr = new T[original.Count];
        original.CopyTo(arr, 0);
        Array.Reverse(arr);
        return new Stack<T>(arr);
    }
}

результаты:

  • Clone1: 318,3766 ms
  • Clone2: 269,2407 ms
  • Clone3: 50,6025 ms
  • Clone4: 37,5233 ms-победитель

Как мы видим, подход, используя копировать, чтобы метод 8 раз быстрее и в то же время реализация довольно проста и понятна. Кроме того, я провел быстрое исследование максимального значения размера стека:Clone3 и Clone4 тесты работали для больших размеров стека перед OutOfMemoryException происходит:

  • Clone1: 67108765 elements
  • Clone2: 67108765 elements
  • Clone3: 134218140 elements
  • Clone4: 134218140 элементы

результаты выше для Clone1 и Clone2 меньше из-за дополнительных коллекций, которые были явно/неявно определены и, следовательно, повлияли на потребление памяти. Таким образом, Clone3 и Clone4 подходы позволяют клонировать экземпляр стека быстрее и с меньшими выделениями памяти. Вы можете получить еще лучшие результаты, используя отражение, но это другая история:)

полная программа листинг можно найти здесь.


Если вам не нужна фактическая Stack`1, но просто хочу быструю копию существующего Stack`1 что вы можете ходить в том же порядке, что Pop возвращает, другой вариант-дублировать Stack`1 на Queue`1. Тогда элементы будут Dequeue в том же порядке, что они будут Pop от оригинала Stack`1.

using System;
using System.Collections.Generic;

class Test
{
  static void Main()
  {
    var stack = new Stack<string>();

    stack.Push("pushed first, pop last");
    stack.Push("in the middle");
    stack.Push("pushed last, pop first");

    var quickCopy = new Queue<string>(stack);

    Console.WriteLine("Stack:");
    while (stack.Count > 0)
      Console.WriteLine("  " + stack.Pop());
    Console.WriteLine();
    Console.WriteLine("Queue:");
    while (quickCopy.Count > 0)
      Console.WriteLine("  " + quickCopy.Dequeue());
  }
}

выход:

Stack:
  pushed last, pop first
  in the middle
  pushed first, pop last

Queue:
  pushed last, pop first
  in the middle
  pushed first, pop last