2011-09-12 19 views
13

Ho pochi stack nel mio codice che uso per tenere traccia della mia posizione logica. In determinati momenti ho bisogno di duplicare gli stack, ma non riesco a clonarli in modo tale da preservare l'ordine. Ho solo bisogno di duplicazione superficiale (riferimenti, non oggetto).C# clona uno stack

Quale sarebbe il modo corretto per farlo? O dovrei usare qualche altro tipo di stack?

NOTA: ho visto questo post Stack Clone Problem: .NET Bug or Expected Behaviour?, ma non sono sicuro di come impostare il metodo clone per la classe Stack.

NOTA # 2: Io uso System.Collection.Generic.Stack

risposta

21
var clonedStack = new Stack<T>(new Stack<T>(oldStack)); 

È possibile scrivere questo come un metodo di estensione come

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

Ciò è necessario perché il costruttore Stack<T> che siamo utilizzando qui è il Stack<T>(IEnumerable<T> source) e, naturalmente, quando si esegue l'iterazione dell'implementazione IEnumerable<T> per un Stack<T>, si apriranno ripetutamente gli elementi dallo stack, in modo da inviarli in ordine inverso rispetto all'ordine e loro di andare sul nuovo stack. Pertanto, eseguendo questo processo due volte, il pacco risulterà nell'ordine corretto.

alternativa:

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

Nuovamente, dobbiamo camminare lo stack in ordine inverso dall'uscita dal iterare su stack.

Dubito che ci sia una differenza di prestazioni tra questi due metodi.

+0

tristemente, stranamente, che non conserva l'ordine :((Suppongo che la doppia inizializzazione di uno stack sia errata o sia un trucco?) – SpaceBear

+0

@Angrius: Conserva l'ordine. La doppia istanziazione è un trucco che causa l'ordine da conservare – jason

+1

@Angrius: la "doppia inizializzazione" è necessaria perché ognuno inverte l'ordine.Se lo si inverte due volte si ottiene l'ordine originale – Gabe

7

Ecco un modo semplice, utilizzando LINQ:

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

È possibile creare un metodo di estensione per rendere questo più facile:

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(); 
+0

Confuso. perché invertire? – SpaceBear

+0

Grazie, ha funzionato alla rovescia semplice! – SpaceBear

+0

ha funzionato per me. Questo conserva anche la pila originale. – MStodd

1

Se don' t necessario un effettivo Stack`1, ma voglio solo una copia veloce di uno esistente Stack`1 che Puoi camminare nello stesso ordine di Pop restituzioni, un'altra opzione è duplicare lo Stack`1 in un Queue`1. Gli elementi saranno quindi Dequeue nello stesso ordine in cui saranno Pop dall'originale 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()); 
    } 
} 

uscita:

 
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

Per coloro che si prendono cura di prestazioni ..Ci sono un paio di altri modi per scorrere i membri dello stack originali, senza grandi perdite nella performance:

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

ho scritto un programma di massima (il link sarà fornito alla fine del post) per misurare le prestazioni e aggiunto due test per le implementazioni già suggerite (vedi replica1 e Clone2), e due test per ToArray e CopyTo approcci (vedi Clone3 e Clone4, ciascuno di essi un uso più efficiente Array.Reverse metodo).

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

I risultati sono:

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

Come noi può vedere, l'approccio usando il metodo CopyTo è 8 volte più veloce e allo stesso tempo l'implementazione è piuttosto semplice e diretta. Inoltre, ho fatto una rapida ricerca su un valore massimo di stack: Clone3 e Clone4 prove lavorato per stack grandi prima OutOfMemoryException verifica:

  • replica1: 67108765 elementi
  • Clone2: 67108765 elementi
  • Clone3: 134218140 elementi
  • Clone4: 134218140 elements

I risultati di cui sopra per replica1 e Clone2 sono più piccole a causa di altre raccolte che erano esplicitamente/implicitamente definiti e pertanto influenzata consumo di memoria. Pertanto, Clone3 e Gli approcci Clone4 consentono di clonare un'istanza di stack più veloce e con meno allocazioni di memoria. Puoi ottenere risultati ancora migliori usando Reflection, ma è una storia diversa :)

L'elenco completo del programma può essere trovato here.