2009-10-24 8 views
23

Mam System.Collections.Generic.Dictionary<A, B> dict, gdzie A i B są klasami i instancją A a (gdzie dict.ContainsKey(a) jest prawdą).Pierwsze KeyValuePair <> bezpośrednio ze Słownika <>

Czy można uzyskać klucz KeyValuePair zawierający a bezpośrednio ze słownika?
Czy muszę utworzyć nową KeyValuePair: new KeyValuePair<A, B>(a, dict[a])?

Odpowiedz

34

Trzeba utworzyć nowy KeyValuePair - należy jednak pamiętać, że jest to rodzaj KVP wartość (struct) W każdym razie, więc to nie jest tak, że wprowadzamy nową nieefektywność w ten sposób. Każda metoda zwracająca KVP i tak tworzyłaby kopię - właśnie tworzysz instancję bezpośrednio.

Zawsze możesz dodać metodę rozszerzenia do IDictionary<TKey, TValue> jeśli chciałeś:

public static KeyValuePair<TKey, TValue> GetEntry 
    (this IDictionary<TKey, TValue> dictionary, 
    TKey key) 
{ 
    return new KeyValuePair<TKey, TValue>(key, dictionary[key]); 
} 

Jak zauważył w komentarzach, to całkiem możliwe, że klucz, który jest przechowywany w słowniku nie jest samo jako jeden pod warunkiem, że jest semantycznie równy - przez jakąś semantykę, która może być dostosowana przez IEqualityComparer (jak na przykład w słowniku niewrażliwym na wielkość liter). W takim przypadku powyższy kod nie zwróci faktycznego wpisu w słowniku, ale wpis z klucz, który podałeś, aby sprawdzić. Niestety nie ma wydajnym sposobem znalezienia oryginalnego klucza - trzeba by iteracyjne nad słowniku :(


miałem świadomość, że można iteracyjne nad w słowniku i znaleźć odpowiednią pozycję tamtędy , ale nie widzę powodu, dla którego chciałbyś to zrobić, gdy masz doskonały indeksator, który jest O (1) zamiast O (N.)

+2

Moja linia myślenia brzmiała: "Wygląda na to, że nie mogę uzyskać odniesienia do rzeczywistego obiektu KeyValuePair w słowniku. Zastanawiam się, dlaczego nie? ", Że KeyValuePair jest typem wartości, który jednoznacznie daje odpowiedź: Dzięki Jon – user200783

+6

Niestety, powyższe działa tylko wtedy, gdy klucz i klucz przechowywane w słowniku są semantycznie równe. string, string> 'zwany' dic' skonstruowany z 'StringComparer.InvariantCultureIgnoreCase', który zawiera {" Jon "," Skeet "}, a następnie' dic.GetEntry ("JON") 'zwróci {" JON "," Skeet "} Jedyny sposób, w jaki wiem, aby słownik powrócił, jest kluczem do sprawdzenia, czy klucz jest częścią wartości. Wydaje się głupie, ale nie znam żadnej alternatywy: – supercat

+2

@ glassasius: Naprawdę chciałbym, aby firma Sun i Microsoft rozpoznały to w wielu przypadkach warto mieć dokładny klucz zapisany w słowniku lub zestawie.Brak takiej zdolności jest szczególnie szkodliwy dla 'WeakHashMap' Java'a [Widzę silniejsze przypadki użycia dla' WeakIdentityHashMap' lub 'WeakReflexiveSet' [który mapuje klucze do siebie] niż dla' WeakHashSet', który nie jest oparty na tożsamości. nie można łatwo odczytać zapisanych wartości klucza. – supercat

13

Jako Dictionary<TKey, TValue> implementuje IEnumerable<KeyValuePair<TKey, TValue>>, możesz użyć linq:

var pair = _dictionary.SingleOrDefault(p => p.Key == myKey); 
+6

No tak, ty * możesz * to zrobić ... ale jest to niewiarygodnie nieefektywne w porównaniu z po prostu przyniesieniem klucza. (Zakłada się również, że == zostało przeciążone dla typu klucza i że słownik używa tego porównania.) Dlaczego chciałbyś to zrobić zamiast tworzyć nowy KVP bezpośrednio? –

+3

to źle! bardzo zły kod tego unikaj !!! rozważ słownik 9999999 pozycji, możesz przejść bezpośrednio do przedmiotu i utworzyć tę parę, lub przejść przez wiele przedmiotów i zdobyć ... Pomyśl o tym .. –

+0

Korzystanie z Linq niekoniecznie musi być złe i nie zleca prezentację za pośrednictwem wszystkich elementów. Może wykorzystywać wewnętrzne przyspieszenie, aby uzyskać pojedynczą KeyValuePair. Jeśli używasz foreach do iteracji, to chyba złe kodowanie! – Adarsha

1

Nie możemy dostać się do "IPHone" ten sposób:

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) 
       { 
        { "IPHone", "TCP/IP honing tools" } 
       }; 

Console.WriteLine(dict["iPhone"]); // "TCP/IP honing tools" 
Console.WriteLine(???); // "IPHone" 

Nie wydaje się nie być O (1) rozwiązanie z aktualnym API, ale zapętlenie poprzez wszystkie wpisy działa:

var keyValue = dict.First(p => dict.Comparer.Equals(p.Key, "iPhone")); 

Console.WriteLine(keyValue.Key); // "IPHone" 
Console.WriteLine(keyValue.Value); // "TCP/IP honing tools" 

lub jako rozszerzenie dla leniwych:

[Pure] 
public static KeyValuePair<TKey, TValue> GetEntry<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key) 
{ 
    var comparer = dictionary.Comparer; 
    return dictionary.FirstOrDefault(p => comparer.Equals(p.Key, key)); 
} 
0

dla mnie myDict.AsEnumerable robi ...

+0

Uwaga: 'AsEnumerable' jest rzeczywiście jednym ze sposobów" przechodzenia przez wszystkie wpisy ", ale nie rozwiązuje pierwotnego pytania, czyli czy istnieje API, które pozwoli ci uzyskać dokładny klucz, * bez * przechodzenia przez wszystkie wpisy. [Do czego odpowiedź wciąż brzmi "Nie".] – ToolmakerSteve

Powiązane problemy