如何克隆 Stack

发布于 2024-12-04 07:59:46 字数 497 浏览 1 评论 0原文

我的代码中有几个堆栈用于跟踪我的逻辑位置。在某些时候,我需要复制堆栈,但我似乎无法以保留顺序的方式克隆它们。我只需要浅层复制(引用,而不是对象)。

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

我看到这篇文章 堆栈克隆问题:.NET Bug 或预期行为?< /a>,但不确定如何为 Stack 类设置克隆方法。

我使用 System.Collection.Generic .Stack

I have few stacks in my code that I use to keep track of my logical location. At certain times I need to duplicate the stacks, but I can't seem to clone them in such way that it preserves the order. I only need shallow duplication (references, not object).

What would be the proper way to do it? Or should I use some other sort of stacks?

I saw this post Stack Clone Problem: .NET Bug or Expected Behaviour?, but not sure how to setup clone method for the Stack<T> class.

I use System.Collection.Generic.Stack<T>.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

勿忘心安 2024-12-11 07:59:46
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 构造函数是 Stack(IEnumerablesource)当然,当您迭代时Stack 的 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 = new Stack<T>(new Stack<T>(oldStack));

You can write this as an extension method as

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

This is necessary because the Stack<T> constructor that we are using here is Stack<T>(IEnumerable<T> source) and of course when you iterate over the IEnumerable<T> implementation for a Stack<T> it is going to repeatedly pop items from the stack thus feeding them to you in reverse of the order that you want them to go onto the new stack. Therefore, doing this process twice will result in the stack being in the correct order.

Alternatively:

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());
}

Again, we have to walk the stack in the reverse order from the output from iterating over the stack.

I doubt there is a performance difference between these two methods.

自控 2024-12-11 07:59:46

对于那些关心性能的人来说..还有其他一些方法可以在不造成性能损失的情况下迭代原始堆栈成员:

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

我编写了一个粗略的程序(链接将在帖子末尾提供)来测量性能并为已建议的实现添加了两个测试(请参阅Clone1Clone2),以及针对ToArrayCopyTo 方法(参见Clone3Clone4,它们都使用更高效的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);
    }
}

结果是:

  • Clone1:318,3766 ms
  • Clone2:269,2407 ms
  • Clone3:50,6025 ms
  • Clone4:37,5233 ms - 获胜者

正如我们所看到的,使用 CopyTo 的方法 方法速度快了 8 倍,同时实现也非常简单明了。此外,我对堆栈大小的最大值进行了快速研究:在发生 OutOfMemoryException 之前,Clone3Clone4 测试适用于更大的堆栈大小:

  • Clone1 :67108765个元素
  • 克隆2:67108765个元素
  • 克隆3:134218140个元素
  • 克隆4: 134218140 个元素

上述 Clone1Clone2 的结果较小,因为显式/隐式定义了额外的集合,因此影响了内存消耗。因此,Clone3Clone4 方法允许更快地克隆堆栈实例并且占用更少的内存分配。使用反射可以获得更好的结果,但这是一个不同的故事:)

完整的程序列表可以在这里找到< /a>.

For those who take care of performance.. There are a few other ways how to iterate through the original stack members without big losses in the performance:

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

I wrote a rough program (the link will be provided at the end of the post) to measure performance and added two tests for the already suggested implementations (see Clone1 and Clone2), and two tests for ToArray and CopyTo approaches (see Clone3 and Clone4, both of them use more efficient Array.Reverse method).

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);
    }
}

The results are:

  • Clone1: 318,3766 ms
  • Clone2: 269,2407 ms
  • Clone3: 50,6025 ms
  • Clone4: 37,5233 ms - the winner

As we can see, the approach using CopyTo method is 8 times faster and at the same time the implementation is pretty simple and straightforward. Moreover, I did a quick research on a maximum value of stack size: Clone3 and Clone4 tests worked for bigger stack sizes before OutOfMemoryException occurs:

  • Clone1: 67108765 elements
  • Clone2: 67108765 elements
  • Clone3: 134218140 elements
  • Clone4: 134218140 elements

The results above for Clone1 and Clone2 are smaller due to additional collections that were explicitly/implicitly defined and therefore affected memory consumption. Thus, Clone3 and Clone4 approaches allow to clone a stack instance faster and with less memory allocations. You can gain even better results using Reflection, but it's a different story :)

The full program listing can be found here.

相守太难 2024-12-11 07:59:46

这是一种简单的方法,使用 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();

Here's one easy way, using LINQ:

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

You could create an extension method to make this easier:

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

Usage:

var clone = originalStack.Clone();
不气馁 2024-12-11 07:59:46

如果您不需要实际的 Stack`1,而只是想要现有 Stack`1 的快速副本,您可以按照与 相同的顺序遍历Pop 返回,另一个选择是将 Stack`1 复制到 Queue`1 中。然后,这些元素将以与从原始 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

If you don't need an actual Stack`1, but just want a quick copy of an existing Stack`1 that you can walk in the same order that Pop returns, another option is to duplicate the Stack`1 into a Queue`1. The elements will then Dequeue in the same order that they'll Pop from the original 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());
  }
}

Output:

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

Queue:
  pushed last, pop first
  in the middle
  pushed first, pop last
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文