2013-04-15 14 views
6

C'è un modo per rimuovere una voce da un Dictionary (per Key) E recuperare è Value in "lo stesso passo?"Rimuovere dal dizionario per chiave e recuperare valore

Per esempio, io sto chiamando

Dictionary.Remove(Key); 

ma voglio anche per restituire il valore allo stesso tempo. La funzione restituisce solo un bool.

So che posso fare qualcosa di simile

Value = Dictionary[Key]; 
Dictionary.Remove(Key); 

ma sembra che questo cercherà il dizionario per due volte (una volta per ottenere il valore, e un'altra volta per rimuoverlo dal dizionario). Come posso (se possibile) fare entrambi senza cercare il dizionario due volte?

+8

possibile duplicato di http://stackoverflow.com/questions/15785091/is-there-any-implementation-to-remove-key-key-and-get-the-value-at-the-same-time This thread ha una risposta accettata – Kenneth

+0

Se è anche correlata alle prestazioni, puoi dare un'occhiata alla risposta qui http://stackoverflow.com/questions/1869452/a-faster-replacement-to-the-dictionarytkey-tvalue e vedere se che cancella tutto su – Silvermind

+0

@Kenneth upvote + grazie per il link. – Mash

risposta

1

Perché entrambi hanno il metodo mancante desiderato I provato Microsoft ConcurrentDictionary e C5 dall'università di Copenhagen http://www.itu.dk/research/c5/ e posso dire con, almeno con il mio caso d'uso è stato super lento (intendo 5x - 10x più lento) rispetto al dizionario. Penso che C5 stia ordinando sia le chiavi che i valori tutto il tempo e Concurrent Dictionary è "troppo preoccupato" per il thread chiamante .. Non sono qui per discutere perché queste due incarnazioni del Dizionario siano lente. Il mio algoritmo cercava e sostituiva alcune voci mentre le prime chiavi venivano rimosse e venivano aggiunte nuove chiavi (una sorta di coda) ... L'unica cosa che rimaneva da fare era modificare il dizionario mscorelib originale .Net. Ho scaricato il codice sorgente da Microsoft e incluso la classe Dictionary nel mio codice sorgente. Per compilare ho anche bisogno di trascinare solo la classe HashHelpers e la classe ThrowHelper. Rimaneva solo da commentare alcune righe (ad esempio [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))] e alcuni recuperi di risorse). Ovviamente ho dovuto aggiungere il metodo mancante alla classe copiata. Inoltre, non cercare di compilare il codice sorgente di Microsoft che lo farai per ore, ho avuto la fortuna di farlo funzionare.

public bool Remove(TKey key, out TValue value) 
    { 
     if (key == null) 
     { 
      ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); 
     } 

     if (buckets != null) 
     { 
      int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; 
      int bucket = hashCode % buckets.Length; 
      int last = -1; 
      for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) 
      { 
       if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) 
       { 
        if (last < 0) 
        { 
         buckets[bucket] = entries[i].next; 
        } 
        else 
        { 
         entries[last].next = entries[i].next; 
        } 
        entries[i].hashCode = -1; 
        entries[i].next = freeList; 
        entries[i].key = default(TKey); 
        value = entries[i].value; 
        entries[i].value = default(TValue); 
        freeList = i; 
        freeCount++; 
        version++; 
        return true; 
       } 
      } 
     } 
     value = default(TValue); 
     return false; 
    } 

Infine ho modificato lo spazio dei nomi per System.Collection.Generic.My Nel mio algoritmo ho avuto solo due righe in cui mi stavo il valore di quanto elimina la riga successiva .. sostituita che con il nuovo metodo e ottenuto una costante guadagno di prestazioni del 7% -10%. Spero che aiuti questo caso d'uso e ogni altro caso in cui re-implementare il Dizionario da zero non è solo ciò che si dovrebbe fare.

+0

Quindi, in pratica, modifica la libreria di Microsoft. Questa domanda è stata fuori per un po 'e questa risposta sembra essere buona, così accetterò questa soluzione. Grazie! – Mash

-1

È possibile farlo con un metodo di estensione:

public static string GetValueAndRemove<TKey, TValue>(this Dictionary<int, string> dict, int key) 
{ 
    string val = dict[key]; 
    dict.Remove(key); 
    return val;  
} 

static void Main(string[] args) 
{ 
    Dictionary<int, string> a = new Dictionary<int, string>(); 
    a.Add(1, "sdfg"); 
    a.Add(2, "sdsdfgadfhfg"); 
    string value = a.GetValueAndRemove<int, string>(1); 
} 
+0

Dal momento che Remove è sicuro, vorrei suggerire di verificare se la chiave esiste prima, ma comunque buona idea :) – Silvermind

+1

Grazie per la risposta, ma avrei dovuto essere più chiaro quando ho detto "nello stesso passo". Quello che intendo davvero non è cercare il dizionario due volte. La stringa val = dict [key] 'cercherà il dizionario una volta e il comando dict.Remove (key)' lo cercherà di nuovo. – Mash

+0

@Mash Il dizionario è veramente veloce, quindi per quanto riguarda le prestazioni non dovrebbe essere un grosso problema. – Silvermind

-1

è possibile estendere la classe di aggiungere tale funzionalità:

public class PoppableDictionary<T, V> : Dictionary<T, V> 
{ 
    public V Pop(T key) 
    { 
     V value = this[key]; 
     this.Remove(key); 
     return value; 
    } 
} 
+1

Ciò richiede ancora due ricerche. L'OP ha richiesto un metodo per eliminare la doppia ricerca – Kenneth

+0

Hai ragione. Ho risposto basandomi sul commento di @ Guvante che afferma che le operazioni di accesso ai dizionari sono o (1) quindi non c'è alcuna penalità significativa nelle prestazioni nell'accedere al dizionario due volte. – mmutilva

+1

@Toto Sì, cercavo principalmente di evitare la doppia ricerca nel dizionario, ma grazie per avermi fornito qualcosa in cui posso almeno farlo in una sola chiamata (upvote). – Mash

0

Anche se questo non è ciò che il PO ha chiesto, io non ho potuto fare a meno di postare un metodo di estensione corretto:

public static bool Remove<TKey, TValue>(this Dictionary<TKey, TValue> self, TKey key, out TValue target) 
{ 
    self.TryGetValue(key, out target); 
    return self.Remove(key); 
} 
1

Il concurrentDictionary ha un metodo TryRemove che tenta di rimuovere e restituire il valore che ha la chiave specificata dal System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>. Si restituisce il valore di default del tipo TValue se chiave non esiste.

https://msdn.microsoft.com/en-us/library/dd287129(v=vs.110).aspx

Problemi correlati