2009-10-24 12 views
23

Ho System.Collections.Generic.Dictionary<A, B> dict dove A e B sono classi e un'istanza A a (dove dict.ContainsKey(a) è vero).Ottenere una KeyValuePair <> direttamente da un dizionario <>

E 'possibile ottenere il KeyValuePair contenente a direttamente dal Dizionario?
Oppure è necessario creare una nuova KeyValuePair: new KeyValuePair<A, B>(a, dict[a])?

risposta

34

È necessario creare un nuovo KeyValuePair - ma tenere a mente che KVP è un tipo di valore (una struct) comunque, quindi non è come si sta introducendo un nuovo inefficienza in questo modo. Qualsiasi metodo che restituisce un KVP dovrebbe comunque creare una copia: stai solo creando l'istanza direttamente.

Si può sempre aggiungere un metodo di estensione per IDictionary<TKey, TValue> se si voleva:

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

Come notato nei commenti, è del tutto possibile che la chiave che è memorizzata nel dizionario non è la stessa come quello fornito, solo semanticamente uguale - da una semantica che potrebbe essere personalizzata da un IEqualityComparer (come con un dizionario insensibile alle maiuscole, per esempio.) In tal caso, il codice sopra non restituirebbe la voce effettiva nel dizionario, ma una voce con la chiave che hai fornito per cercare. Purtroppo non c'è modo efficace di trovare la chiave originale - che avrebbe dovuto iterare dizionario :(


ero consapevole che si potrebbe iterare le voci del dizionario e trovare la voce appropriata in questo modo , ma non vedo alcuna ragione per cui tu voglia mai farlo quando hai un indicizzatore perfettamente buono che è O (1) invece di O (N).

+2

La mia linea di pensiero era: "Sembra che non riesca a ottenere un riferimento all'oggetto KeyValuePair reale all'interno del dizionario. Mi chiedo perché no? ". KeyValuePair è un tipo di valore che risponde chiaramente. Grazie Jon – user200783

+6

Sfortunatamente, quanto sopra funziona solo se il parametro chiave e la chiave memorizzati nel dizionario sono semanticamente uguali. stringa, stringa> 'chiamata' dic' costruita con 'StringComparer.InvariantCultureIgnoreCase' che contiene {" Jon "," Skeet "}, quindi' dic.GetEntry ("JON") 'restituisce {" JON "," Skeet "} L'unico modo che conosco per fare in modo che un dizionario restituisca la chiave di una ricerca è che la chiave sia parte del valore. Sembra stupida, ma non conosco alternative – supercat

+2

@eglasius: Spero davvero che Sun e Microsoft lo abbiano riconosciuto in molti casi è utile avere la chiave esatta che è memorizzata in un dizionario o in un set.La mancanza di tale capacità è particolarmente dannosa per Java 'WeakHashMap' [Vedo casi di utilizzo più intensi per un' WeakIdentityHashMap', o 'WeakReflexiveSet' [che mappa le chiavi a se stessi] piuttosto che per un' WeakHashSet 'non basato sull'identità che non può leggere prontamente i valori chiave memorizzati. – supercat

13

Come Dictionary<TKey, TValue> strumenti IEnumerable<KeyValuePair<TKey, TValue>>, è possibile utilizzare linq:

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

Beh sì, tu * potresti * farlo ... ma è incredibilmente inefficiente rispetto al semplice recupero della chiave. (Si presuppone inoltre che == sia stato sovraccaricato per il tipo di chiave e che il dizionario stia utilizzando tale confronto.) Perché dovresti volerlo fare invece di creare direttamente un nuovo KVP? –

+3

è sbagliato !!! codice molto brutto evitarlo !!! considera un dizionario di 9999999 articoli, puoi andare direttamente al tuo articolo e creare questa coppia, o passare attraverso molti articoli e ottenerlo ... pensarci .. –

+0

Usare Linq potrebbe non essere necessariamente cattivo, e non impone un'allusione attraverso tutti gli elementi. Potrebbe utilizzare l'hastable interno per ottenere il singolo KeyValuePair. Se stai usando un foreach per iterare, allora questo è un codice errato! – Adarsha

1

Non possiamo arrivare a "IPHone" questo modo:

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

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

C'è sembra esserci O (1) soluzione con l'API di corrente, ma scorrendo tutte le voci funziona:

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

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

O come un'estensione per i pigri:

[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

per me myDict.AsEnumerable lo fa ...

+0

Nota: 'AsEnumerable' è in effetti un modo per" scorrere tutte le voci ", ma non risolve la domanda originale, ovvero se esiste un'API che ti consente di ottenere la chiave esatta, * senza * passare attraverso tutte le voci. [A cui la risposta è ancora "No".] – ToolmakerSteve

Problemi correlati