C# 克隆一个堆栈

C# 克隆一个堆栈,c#,.net,stack,clone,C#,.net,Stack,Clone,在我的代码中,我很少使用堆栈来跟踪我的逻辑位置。在某些时候,我需要复制堆栈,但我似乎不能以这样的方式克隆它们,以保持顺序。我只需要浅复制(引用,而不是对象) 正确的方法是什么?或者我应该使用其他类型的堆栈 注意:我看到了这篇文章,但不确定如何为Stack类设置clone方法 注意#2:我使用System.Collection.Generic.Stackvar clonedStack=newstack(newstack(oldStack)); var clonedStack = new Stack

在我的代码中,我很少使用堆栈来跟踪我的逻辑位置。在某些时候,我需要复制堆栈,但我似乎不能以这样的方式克隆它们,以保持顺序。我只需要浅复制(引用,而不是对象)

正确的方法是什么?或者我应该使用其他类型的堆栈

注意:我看到了这篇文章,但不确定如何为Stack类设置clone方法

注意#2:我使用System.Collection.Generic.Stack

var clonedStack=newstack(newstack(oldStack));
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));
}
公共静态堆栈克隆(此堆栈){
Contract.Requires(stack!=null);
返回新堆栈(新堆栈(堆栈));
}
这是必要的,因为我们在这里使用的
堆栈
构造函数是,当然,当您迭代
堆栈
IEnumerable
实现时,它将重复从堆栈中弹出项,从而按照您希望它们进入新堆栈的相反顺序将它们提供给您。因此,两次执行此过程将导致堆栈的顺序正确

或者:

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());
}
var clonedStack=新堆栈(oldStack.Reverse());
公共静态堆栈克隆(此堆栈){
Contract.Requires(stack!=null);
返回新堆栈(Stack.Reverse());
}
同样,我们必须按照与迭代堆栈的输出相反的顺序遍历堆栈


我怀疑这两种方法之间是否存在性能差异。

这里有一种简单的方法,使用LINQ:

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

如果您不需要实际的
Stack`1
,只需要一个现有
Stack`1
的快速副本,可以按照
Pop
返回的相同顺序运行,另一个选项是将
Stack`1
复制到
队列`1
中。然后,这些元素将按照从原始
堆栈`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());
  }
}
使用系统;
使用System.Collections.Generic;
课堂测试
{
静态void Main()
{
var stack=新堆栈();
stack.Push(“先推,后弹出”);
堆叠。推动(“中间”);
stack.Push(“最后推,先弹出”);
var quickCopy=新队列(堆栈);
Console.WriteLine(“堆栈:”);
而(stack.Count>0)
Console.WriteLine(“+stack.Pop());
Console.WriteLine();
控制台写入线(“队列:”);
而(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);
public T[]Stack.ToArray();
public void Stack.CopyTo(T[]数组,int-arrayIndex);
我编写了一个粗略的程序(链接将在文章末尾提供)来衡量性能,并为已经建议的实现添加了两个测试(参见Clone1和Clone2),以及两个测试ToArrayCopyTo方法(参见Clone3和Clone4,它们都使用更有效的数组。反向方法)

公共静态类堆栈扩展
{
公共静态堆栈克隆1(此堆栈原始)
{
返回新堆栈(新堆栈(原始));
}
公共静态堆栈克隆2(此堆栈原始)
{
返回新堆栈(original.Reverse());
}
公共静态堆栈克隆3(此堆栈原始)
{
var arr=original.ToArray();
阵列。反向(arr);
返回新堆栈(arr);
}
公共静态堆栈克隆4(此堆栈原始)
{
var arr=新的T[原始计数];
原件。复印件(arr,0);
阵列。反向(arr);
返回新堆栈(arr);
}
}
结果是:

  • 克隆1:3183766毫秒
  • 克隆2:2692407毫秒
  • 克隆3:506025毫秒
  • 克隆4:375233毫秒-获胜者
正如我们所看到的,使用CopyTo方法的方法速度快了8倍,同时实现非常简单和直接。此外,我还快速研究了堆栈大小的最大值:在OutOfMemoryException发生之前,Clone3和Clone4测试适用于更大的堆栈大小:

  • 克隆1:67108765个元素
  • 克隆2:67108765个元素
  • 克隆3:134218140个元素
  • 克隆4:134218140个元素
由于显式/隐式定义了额外的集合,因此Clone1和Clone2的上述结果较小,从而影响了内存消耗。因此,Clone3和Clone4方法允许更快地克隆堆栈实例,并且使用更少的内存分配。使用反射可以获得更好的结果,但情况不同:)


可以找到完整的程序列表。

遗憾的是,奇怪的是,这并没有保留顺序:((我假设堆栈的双重初始化是打字错误还是一个技巧?)@Angrius:它确实保留了顺序。双重实例化是一个导致顺序被保留的技巧。@Angrius:双重初始化”是必需的,因为每一个都会反转顺序。如果反转两次,则会得到原始顺序。@Angrius:另外,它不保留顺序也不奇怪。请参阅我对其发生原因的解释。
IEnumerable
for
Stack
的实现非常有意义;如果
IEnumerable的实现e> 对于
Stack
来说,除了重复弹出和输出之外,我什么都做了。对于
Stack(IEnumerable source)
来说,构造函数非常有意义;如果
Stack
的构造函数除了从
源代码中拉出和推送之外,什么都做了,我会感到困惑。这一点都不奇怪
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);
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);
    }
}