2013-04-15 16 views
6

Czy istnieje sposób usunięcia wpisu z numeru Dictionary (przez Key) ORAZ odzyskanie go w postaci "tego samego kroku?" Pod numerem Value.Usuń ze słownika kluczem i pobierz wartość

Na przykład, dzwonię

Dictionary.Remove(Key); 

ale chcę też zwrócić wartość w tym samym czasie. Funkcja zwraca tylko bool.

wiem, że mogę zrobić coś takiego

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

ale wydaje się, że to będzie przeszukać słownika dwa razy (raz, aby uzyskać wartość, a innym razem, aby usunąć go ze słownika). Jak mogę (jeśli to możliwe) wykonać oba BEZ dwukrotnego przeszukiwania słownika?

+8

możliwy duplikat http://stackoverflow.com/questions/15785091/is-there-any-implementation-to-remove-by-key-and-get-the-value-at-the-same-time This wątek ma akceptowaną odpowiedź – Kenneth

+0

Jeśli jest to związane z wydajnością, możesz rzucić okiem na odpowiedź tutaj: http://stackoverflow.com/questions/1869452/a-faster-replacement-to-tiction-tictionarytkey-tvalue i sprawdzić, czy to usuwa cokolwiek wyżej – Silvermind

+0

@Kenneth upvote + dzięki za link. – Mash

Odpowiedz

1

Ponieważ obie mają pożądaną brakującą metodę wypróbowałem Microsoft's ConcurrentDictionary i C5 z University of Copenhagen http://www.itu.dk/research/c5/ i mogę powiedzieć, przynajmniej z moim przypadkiem użycia był super wolny (mam na myśli 5x - 10x wolniejszy) w porównaniu do Dictionary. Myślę, że C5 cały czas sortuje klucze i wartości, a Słownik współbieżności jest "zbyt zmartwiony" w wątku wywołującym. Nie jestem tutaj, żeby omówić, dlaczego te dwie inkarnacje Słownika są wolne. Mój algorytm szukał i zastępował niektóre wpisy, podczas gdy pierwsze klucze zostałyby usunięte, a nowe klucze zostały dodane (jakaś kolejka) ... Pozostało tylko zmodyfikować oryginalny słownik .NET mscorelib. Pobrałem kod źródłowy z Microsoft i uwzględniłem klasę Dictionary w moim kodzie źródłowym. Aby skompilować, muszę też przeciągnąć tylko klasę HashHelpers i ThrowHelper. Pozostało tylko skomentować niektóre linie (np. [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))] i trochę pobierania zasobów). Oczywiście musiałem dodać brakującą metodę do skopiowanej klasy. Nie próbuj też kompilować kodu źródłowego Microsoft, który będziesz robił przez wiele godzin. Miałem szczęście, że udało mi się go uruchomić.

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

Wreszcie ja zmodyfikował nazw do System.Collection.Generic.My W moim algorytmu miałem tylko dwie linie, gdzie byłem coraz wartość niż usunięcie go w następnej linii .. zastąpiony że z nowej metody i uzyskano stały wzrost wydajności o 7% -10%. Mam nadzieję, że pomaga to temu przypadkowi użycia i innym przypadkom, w których ponowne wprowadzenie Słownika od podstaw nie jest po prostu tym, co należy zrobić.

+0

Po prostu zmodyfikuj bibliotekę Microsoftu. To pytanie już dawno minęło, a ta odpowiedź wydaje się być tak dobra, jak to tylko możliwe, więc przyjmuję to rozwiązanie. Dziękuję Ci! – Mash

-1

Można to zrobić za pomocą metody Extension:

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

Ponieważ Remove jest bezpieczny, sugerowałbym sprawdzenie, czy klucz istnieje pierwszy, ale i tak dobry pomysł :) – Silvermind

+1

Dziękuję za odpowiedź, ale powinienem być bardziej jasne, kiedy powiedziałem "w tym samym kroku". Co naprawdę mam na myśli, to nie przeszukiwać słownika dwa razy. 'String val = dict [key]' przeszuka słownik raz, a 'dict.Remove (key)' przeszuka go ponownie. – Mash

+0

@Mash Dictionary jest naprawdę szybki, więc pod względem wydajności nie powinien być dużym problemem. – Silvermind

-1

można rozszerzyć klasę, aby dodać tę funkcjonalność:

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

To nadal wymaga dwóch wyszukiwań. OP zażądał metody wyeliminowania podwójnego wyszukiwania – Kenneth

+0

Masz rację. Odpowiedziałem na podstawie komentarza @ Guvante, który stwierdza, że ​​operacje dostępu do słowników są o (1), więc nie ma znaczącej obniżki wydajności przy dwukrotnym dostępie do słownika. – mmutilva

+1

@Toto Tak, głównie szukałem uniknięcia podwójnego wyszukiwania w słowniku, ale dziękuję za dostarczenie czegoś, co mogę zrobić przynajmniej w jednym wywołaniu (upvote). – Mash

0

Choć nie jest to co PO poprosił o, ja nie mogłem sobie pomóc, ale opublikowałem poprawioną metodę rozszerzenia:

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); 
} 
Powiązane problemy