2011-06-20 10 views
10

Jestem byłym C++/STL programista próbuje zakodować szybki algorytm marszu przy użyciu C# /. NET ...Znajdź albo włożyć tylko jeden odnośnika w C# słowniku

Szukam odpowiednik metody STL "map :: insert", która wstawi wartość w danym kluczu, jeśli nie istnieje, w przeciwnym razie zwraca iterator do istniejącej pary klucz-wartość.

Jedynym sposobem znalazłem robi to z dwóch wyszukiwań: jeden wewnątrz TryGetValue a drugi w metodzie Add:

List<Point> list; 
if (!_dictionary.TryGetValue (pcost, out list)) 
{ 
    list = new List<Point>(); 
    dictionary.Add (pcost, list); 
} 
list.Add (new Point { X = n.x, Y = n.y }); 

Czy istnieje coś, co wyjaśnia, dlaczego nie jest to możliwe przy użyciu kontenerów .NET? A może przegapiłem jakiś punkt?

Dzięki.

+1

Czy na pewno wykonuje dwa wyszukiwania nawet w kodzie produkcyjnym? – CodingBarfield

+0

Czy podwójny wygląd naprawdę ma znaczenie? Różnica w czasie jest marginalna. –

+4

@ Chris: co? nie masz żadnego benchmarku, aby to poprzeć, nie mówiąc już o benchmarku stosownym do wzorców użycia OP;) - Mogę pokazać ci kod tam, gdzie ma to znaczenie (och, czekaj, nie mogę z powodów prawnych ...) – sehe

Odpowiedz

-2

Można utworzyć metodę rozszerzenia dla że:

IDictionary<string, Point> _dictionary = GetDictionary(); 
_dictionary.GetOrAdd("asdf").Add(new Point(14, 15)); 

// ... elsewhere ... 
public static class DictionaryExtensions { 
    public static List<TValue> GetOrAdd<TKey, TValue>(this IDictionary<TKey, List<TValue>> self, TKey key) { 
     List<TValue> result; 
     self.TryGetValue(key, out result); 
     if (null == result) { 
      // the key value can be set to the null 
      result = new List<TValue>(); 
      self[key] = result; 
     } 

     return result; 
    } 
} 
+1

Zakładasz, że TValue jest tam typem odniesienia, powinieneś zamiast tego zwracać uwagę na wartość zwracaną przez TryGetValue. –

+3

To jest ten sam kod, co w pytaniu, ale dopiero teraz wprowadzono rozszerzenie. Jak to pomaga? Nadal 2 połączenia, trygetvalue i dodaj. – RvdK

+0

@PoweRoy: aby być uczciwym, wydajność zostanie zakłócona (po prostu nie we właściwym kierunku). – sehe

10

można tylko przypisać wartość w następujący sposób:

var dict = new Dictionary<int, int>(); 
dict[2] = 11; 

jeśli wartość z klucza 2 nie istnieje - zostanie on dodany i w przeciwnym razie zostanie tylko przesłonięty.

słownik nie ma metody GetOrAdd, ale ConcurrentDictionary z C# 4.0 czyni:

var dict = new ConcurrentDictionary<int, int>(); 
dict[2] = 10; 
int a = dict.GetOrAdd(2, 11);// a == 10 
+0

Doskonała informacja, gorik – sehe

+0

@gorik używając słownika, który pokazałeś, rzuci wyjątek, jeśli klucz nie istnieje – gerstla

+3

@amichai gerstl nie, spróbuj go wypróbować –

2

Standard rodzajowy słowniku nie obsługuje tej funkcji, wymagane są 2 wyszukiwań. Chociaż koszty wyszukiwań są zwykle pomijalne, więc nie stanowi to problemu, a często można uzyskać lepsze rezultaty, dostrajając inne części systemu, zamiast próbować mikrooptymalizować wyszukiwanie słownika.

Jedynym słownikiem, który jest dostarczany z .NET, który obsługuje to, co znam, jest ConcurrentDictionary z metodą GetOrAdd. Chociaż teraz płacisz koszt synchronizacji.

+0

false, sprawdź tę odpowiedź: http://stackoverflow.com/a/16193323/893406 tylko jedno wyszukiwanie. –

+1

@ v.oddou wywołanie polecenia 'dict.Add' wewnętrznie wykonuje wyszukiwanie, stąd 2 wyszukiwania. Jedno spojrzenie przez TryGetValue i jedno przez Add. –

+0

ah tak. cholernie. –

2

Czy istnieje coś, co wyjaśnia, dlaczego nie jest to możliwe przy użyciu .NET pojemniki?

Nie znając prawdziwego tła, zakładam, że jest tak ze względu na prostotę Słownika. Są tylko podstawowe, łatwe do zrozumienia funkcje: Add, Remove a.s.o., podczas gdy operator indeksu wykonuje trochę magii, która prawdopodobnie była intuicyjna.

2

Niestety, nie ma jednego w implementacji bcl. Najbliższa alternatywa robi dwa wyszukiwań, ale można mieć ogólną metodę rozszerzenia, aby ułatwić, as shown here

public static T GetOrAdd<S, T>(this IDictionary<S, T> dict, S key, 
           Func<T> valueCreator) 
{ 
    T value; 
    return dict.TryGetValue(key, out value) ? value : dict[key] = valueCreator(); 
} 

Ale jest C5's implementation który robi to po wyjęciu z pudełka. Definicja metoda wygląda następująco:

public virtual bool FindOrAdd(K key, ref V value) 
{ 

} 

Nie wiem, dlaczego nie przyjąć Func<V> zamiast V odroczyć tworzenia obiektów.C5 ma wiele fajnych podobnych trików, na przykład:

public virtual bool Remove(K key, out V value) 

public virtual bool Update(K key, V value, out V oldvalue) 

public virtual bool UpdateOrAdd(K key, V value, out V oldvalue)