我的代码中有几个堆栈用于跟踪我的逻辑位置。在某些时候,我需要复制堆栈,但似乎无法以保留顺序的方式克隆它们。我只需要浅层重复(引用,而不是对象)。c#克隆一个堆栈
什么是正确的方法来做到这一点?或者我应该使用其他类型的堆栈?
注意:我看到这篇文章Stack Clone Problem: .NET Bug or Expected Behaviour?,但不知道如何为Stack类设置克隆方法。
注2:我用System.Collection.Generic.Stack
我的代码中有几个堆栈用于跟踪我的逻辑位置。在某些时候,我需要复制堆栈,但似乎无法以保留顺序的方式克隆它们。我只需要浅层重复(引用,而不是对象)。c#克隆一个堆栈
什么是正确的方法来做到这一点?或者我应该使用其他类型的堆栈?
注意:我看到这篇文章Stack Clone Problem: .NET Bug or Expected Behaviour?,但不知道如何为Stack类设置克隆方法。
注2:我用System.Collection.Generic.Stack
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)
,当然,当您迭代Stack<T>
的IEnumerable<T>
实现时,它将重复弹出堆栈中的项目,从而将它们反馈给您,顺序与您w蚂蚁他们去新的堆栈。因此,执行此过程两次将导致堆栈按正确顺序排列。
或者:
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();
如果你不”吨需要一个实际的Stack`1
,但只想快速复制一个现有的Stack`1
y您可以按照Pop
返回的顺序行走,另一个选项是将Stack`1
复制到Queue`1
。这些元素将以Dequeue
的顺序与原来的Stack`1
的Pop
相同。
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
对于那些谁拿的性能照顾..还有一些其他的方式如何通过无需在性能损失就大了原来的堆叠成员重复:
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异常工作之前更大的堆栈大小发生:
上述用于Clone1和Clone2结果,要由显式/隐式定义的,并因此影响了存储器消耗的附加集合是较小的。因此,Clone3和Clone4方法允许更快克隆栈实例和使用较少的内存分配。你可以得到使用反射更好的结果,但它是一个不同的故事:)
完整的程序列表中可以发现here。
令人遗憾的是,奇怪的是,这并不能保持顺序:((我认为双栈初始化是一个错误或是一个诡计?) – SpaceBear
@Angrius:它确实保存了顺序,双重实例化是一个技巧,被保留的顺序 – jason
@Angrius:因为每一个颠倒的顺序“双初始化”要求如果颠倒两次你原来的顺序 – Gabe