2011-09-12 112 views
13

我的代碼中有幾個堆棧用於跟蹤我的邏輯位置。在某些時候,我需要複製堆棧,但似乎無法以保留順序的方式克隆它們。我只需要淺層重複(引用,而不是對象)。c#克隆一個堆棧

什麼是正確的方法來做到這一點?或者我應該使用其他類型的堆棧?

注意:我看到這篇文章Stack Clone Problem: .NET Bug or Expected Behaviour?,但不知道如何爲Stack類設置克隆方法。

注2:我用System.Collection.Generic.Stack

回答

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

同樣,我們必須從遍歷堆棧遍歷堆棧以相反的順序從輸出。

我懷疑這兩種方法之間存在性能差異。

+0

令人遺憾的是,奇怪的是,這並不能保持順序:((我認爲雙棧初始化是一個錯誤或是一個詭計?) – SpaceBear

+0

@Angrius:它確實保存了順序,雙重實例化是一個技巧,被保留的順序 – jason

+1

@Angrius:因爲每一個顛倒的順序「雙初始化」要求如果顛倒兩次你原來的順序 – Gabe

7

這裏有一個簡單的方法,使用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(); 
+0

困惑。爲什麼會反向 – SpaceBear

+0

謝謝,簡單的逆向工作! – SpaceBear

+0

爲我工作。這也保留了原始堆棧。 – MStodd

1

如果你不」噸需要一個實際的Stack`1,但只想快速複製一個現有的Stack`1 y您可以按照Pop返回的順序行走,另一個選項是將Stack`1複製到Queue`1。這些元素將以Dequeue的順序與原來的Stack`1Pop相同。

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 
2

對於那些誰拿的性能照顧..還有一些其他的方式如何通過無需在性能損失就大了原來的堆疊成員重複:

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

我寫了一個粗略的方案(該鏈接將在帖子的末尾提供)來衡量性能和增加了兩個試驗已經表明實現(見Clone1Clone2),和兩個試驗ToArray的CopyTo從方法(見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毫秒
  • Clone2:269,2407毫秒
  • Clone3:50,6025毫秒
  • Clone4:37,5233 ms - 贏家

因爲我們可以看到,使用CopyTo從方法的方法是快8倍,並在同一時間實現是非常簡單和直接。此外,我做了一個快速研究堆大小的最大值:Clone3Clone4測試OutOfMemoryException異常工作之前更大的堆棧大小發生:

  • Clone1:67108765種元素
  • Clone2:67108765元件
  • Clone3:134218140個元件
  • Clone4:134218140個元件

上述用於Clone1Clone2結果,要由顯式/隱式定義的,並因此影響了存儲器消耗的附加集合是較小的。因此,Clone3Clone4方法允許更快克隆棧實例和使用較少的內存分配。你可以得到使用反射更好的結果,但它是一個不同的故事:)

完整的程序列表中可以發現here