我的代码中有很少的堆栈用于跟踪我的逻辑位置。在某些时候我需要复制堆栈,但我似乎无法以保留订单的方式克隆它们。我只需要浅层复制(引用,而不是对象)。
这样做的正确方法是什么?或者我应该使用其他类型的堆栈?
注意:我看过这篇文章Stack Clone Problem: .NET Bug or Expected Behaviour?,但不知道如何为Stack类设置克隆方法。
注意#2:我使用System.Collection.Generic.Stack
答案 0 :(得分:25)
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>
的{{1}}实现进行迭代时,它将会从堆栈中反复弹出项目,从而按照您希望它们进入新堆栈的顺序将它们提供给您。因此,执行此过程两次将导致堆栈处于正确的顺序。
可替换地:
Stack<T>
同样,我们必须以相反的顺序从堆栈中迭代的输出开始堆栈。
我怀疑这两种方法之间存在性能差异。
答案 1 :(得分:8)
这是一种简单的方法,使用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();
答案 2 :(得分:7)
对于那些照顾性能的人来说。还有其他几种方法可以在不损失性能的情况下迭代原始堆栈成员:
public T[] Stack<T>.ToArray();
public void Stack<T>.CopyTo(T[] array, int arrayIndex);
我写了一个粗略的程序(链接将在帖子的末尾提供)来衡量性能,并为已经建议的实现添加了两个测试(参见 Clone1 和 Clone2 ),以及针对 ToArray 和 CopyTo 方法的两个测试(请参阅 Clone3 和 Clone4 ,两者都使用更高效的 Array.Reverse 方法)。
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);
}
}
结果是:
正如我们所看到的,使用 CopyTo 方法的方法快8倍,同时实现非常简单明了。此外,我对堆栈大小的最大值进行了快速研究: Clone3 和 Clone4 测试在 OutOfMemoryException 发生之前适用于更大的堆栈大小:< / p>
Clone1 和 Clone2 的上述结果较小,原因是显式/隐式定义了额外的集合,因此影响了内存消耗。因此, Clone3 和 Clone4 方法允许更快地克隆堆栈实例并减少内存分配。你可以使用Reflection获得更好的结果,但它是一个不同的故事:)
可以找到完整的计划列表here。
答案 3 :(得分:0)
如果您不需要实际的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