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