Cosa succede alla ricerca C # Dictionary <int, int> se la chiave non esiste?


121

Ho provato a verificare la presenza di null ma il compilatore avverte che questa condizione non si verificherà mai. Cosa dovrei cercare?

Risposte:


196

Supponendo che si desidera ottenere il valore se la chiave non esiste, utilizzare Dictionary<TKey, TValue>.TryGetValue:

int value;
if (dictionary.TryGetValue(key, out value))
{
    // Key was in dictionary; "value" contains corresponding value
} 
else 
{
    // Key wasn't in dictionary; "value" is now 0
}

(L'utilizzo di ContainsKeye poi l'indicizzatore fa cercare la chiave due volte, il che è piuttosto inutile.)

Nota che anche se stessi usando i tipi di riferimento, il controllo di null non funzionerebbe: l'indicizzatore di Dictionary<,>genererà un'eccezione se richiedi una chiave mancante, invece di restituire null. (Questa è una grande differenza tra Dictionary<,>e Hashtable.)


@JonSkeet Non è anche TryGetValue a fare una doppia ricerca ( come indicato nel corpo della domanda )?
nawfal

5
@nawfal: non vedo alcuna indicazione che la domanda lo affermi affatto. Dice che sta facendo più lavoro di ContainsKey, il che è vero, perché deve estrarre anche il valore. Tuttavia, non sta facendo due ricerche.
Jon Skeet

Ingenuamente, continuavo ad aspettarmi null, ma per Dictionary <TKey, enum>, questo restituisce l'equivalente "0" nell'enum.
Jess

23

Il dizionario genera KeyNotFoundun'eccezione nel caso in cui il dizionario non contenga la tua chiave.

Come suggerito, ContainsKeyè la precauzione appropriata. TryGetValueè anche efficace.

Ciò consente al dizionario di memorizzare un valore null in modo più efficace. Senza che si comporti in questo modo, il controllo di un risultato nullo dall'operatore [] indicherebbe un valore nullo OPPURE l'inesistenza della chiave di input che non va bene.


Ulteriori informazioni sono disponibili all'indirizzo MSDN: msdn.microsoft.com/en-gb/library/9tee9ht2.aspx
cyberzed

10

Se stai solo controllando prima di provare ad aggiungere un nuovo valore, utilizza il ContainsKeymetodo:

if (!openWith.ContainsKey("ht"))
{
    openWith.Add("ht", "hypertrm.exe");
}

Se stai verificando che il valore esista, utilizza il TryGetValuemetodo descritto nella risposta di Jon Skeet.


8
TryGet è migliore
Ruben Bartelink

2
Perché stai risolvendo la ricerca delle chiavi attraverso la tabella hash due volte se ottieni immediatamente dopo il Contains. Wintellect PowerCollections ha anche GetValueElseAddmetodi a cui si assegna un valore (o un Func<TValue>) per salvare anche la risoluzione sull'inserto se si intende aggiungere se non è presente. Immagino che il motivo per cui non è stato inserito nelle librerie .NET è perché il percorso di
aggiunta

@rub: immagino che dipenda dallo scopo del codice. Se vuoi usare il valore sono d'accordo che TryGetValuesarebbe meglio, ma se vuoi controllare se il dizionario contiene la chiave per evitare aggiunte duplicate, direi che ContainsKeyè altrettanto buono (se non migliore).
Fredrik Mörk

@Fredrik: Se vuoi solo fare un controllo di contenimento, allora sì, vale la pena usare ContainsKey. Nota che questo non è il caso nel codice di esempio di questa risposta.
Jon Skeet

@ Jon: vero, in realtà mi è sfuggito che il valore aggiunto è stato recuperato immediatamente dopo che è stato aggiunto.
Fredrik Mörk

3

Dovresti controllare Dictionary.ContainsKey (int key) prima di provare a estrarre il valore.

Dictionary<int, int> myDictionary = new Dictionary<int, int>();
myDictionary.Add(2,4);
myDictionary.Add(3,5);

int keyToFind = 7;
if(myDictionary.ContainsKey(keyToFind))
{
    myValueLookup = myDictionay[keyToFind];
    // do work...
}
else
{
    // the key doesn't exist.
}

2
Perché vuoi che esegua la ricerca due volte?
Jon Skeet

2
@mookid: non secondo me. L'idea è cercare di cercare la chiave e prendere una linea di condotta se viene trovata e un'altra linea di azione altrimenti, giusto?
Jon Skeet

3
@ Jon - Onestamente? Perché non lo sapevo TryGetValue. Per fortuna, ora lo so, quindi lo saprò in futuro. Lascerò questa risposta intatta, anche se la discussione è preziosa.
ZombieSheep

@ Jon Skeet - È per questo che sono qui. :)
ZombieSheep

@ JonSkeet Perché prima di C # 7, non si poteva usare TryGetValuein un'espressione lambda. Anche se questo mi fa pensare che una nuova estensione a C # sarebbe un catchoperatore simile all'operatore di nullcoalescenza.
NetMage

1

Una classe helper è utile:

public static class DictionaryHelper
{
    public static TVal Get<TKey, TVal>(this Dictionary<TKey, TVal> dictionary, TKey key, TVal defaultVal = default(TVal))
    {
        TVal val;
        if( dictionary.TryGetValue(key, out val) )
        {
            return val;
        }
        return defaultVal;
    }
}

A volte mi chiedo perché questo non sia aggiunto alla libreria standard. Quasi tutte le lingue che usano hashmap restituiscono null se non ci sono voci, non un'eccezione. Un elemento che non esiste nel dizionario non è un comportamento eccezionale.
Adam Hess

@AdamHess - ecco perché hai Hashtable () in c # ... sfortunatamente, le tue chiavi vengono imballate lì ... :(
veljkoz


0

Probabilmente dovresti usare:

if(myDictionary.ContainsKey(someInt))
{
  // do something
}

Il motivo per cui non puoi verificare la presenza di null è che la chiave qui è un tipo di valore.


1
Il tipo di valore è in qualche modo irrilevante, poiché il controllo di null non avrebbe l'effetto desiderato.
Jon Skeet

@Johannes, la soluzione di Jon è ovviamente molto migliore, ma il richiedente ha affermato di aver verificato se la chiave esiste, ed è un Dictionary <int, int>, quindi anche la chiave è un tipo di valore qui.
Razzie

0
int result= YourDictionaryName.TryGetValue(key, out int value) ? YourDictionaryName[key] : 0;

Se la chiave è presente nel dizionario, restituisce il valore della chiave altrimenti restituisce 0.

Spero che questo codice ti aiuti.


1
Se la chiave esiste, questo codice verrà cercato due volte. TryGetValueè sufficiente, usa valueinvece diresult
Mathieu VIALES

0

Considera l'opzione di incapsulare questo particolare dizionario e fornire un metodo per restituire il valore per quella chiave:

public static class NumbersAdapter
{
    private static readonly Dictionary<string, string> Mapping = new Dictionary<string, string>
    {
        ["1"] = "One",
        ["2"] = "Two",
        ["3"] = "Three"
    };

    public static string GetValue(string key)
    {
        return Mapping.ContainsKey(key) ? Mapping[key] : key;
    }
}

Quindi puoi gestire il comportamento di questo dizionario.

Ad esempio qui: se il dizionario non ha la chiave, restituisce la chiave che passi per parametro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.