Co się stanie z wyszukiwaniem w C # Dictionary <int, int>, jeśli klucz nie istnieje?


121

Próbowałem sprawdzić wartość null, ale kompilator ostrzega, że ​​ten warunek nigdy nie wystąpi. Czego powinienem szukać?

Odpowiedzi:


196

Zakładając, że chcesz uzyskać wartość wtedy, gdy klucz nie istnieje, należy użyć 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
}

(Użycie, ContainsKeya następnie indeksatora powoduje dwukrotne sprawdzenie klucza, co jest dość bezcelowe).

Należy pamiętać, że nawet jeśli były przy użyciu typów referencyjnych, sprawdzanie wartości null nie działa - Indexer dla Dictionary<,>rzuci wyjątek, jeśli poprosisz brakującego klucza, zamiast wracać null. (To jest duża różnica między Dictionary<,>a Hashtable.)


@JonSkeet Czy TryGetValue nie wykonuje również podwójnego wyszukiwania ( jak stwierdzono w treści tego pytania )?
nawfal

5
@nawfal: Nie widzę żadnej wskazówki, że to pytanie w ogóle o tym mówi. Mówi, że wykonuje więcej pracy niż ContainsKey, co jest prawdą, ponieważ musi również wydobyć wartość. Nie robi to jednak dwóch wyszukiwań.
Jon Skeet

Naiwnie oczekiwałem wartości null, ale dla Dictionary <TKey, enum> zwraca to w wyliczeniu odpowiednik „0”.
Jess

23

Słownik zgłasza KeyNotFoundwyjątek w przypadku, gdy słownik nie zawiera klucza.

Jak sugerowano, ContainsKeyjest to odpowiednie środki ostrożności. TryGetValuejest również skuteczny.

Dzięki temu słownik może efektywniej przechowywać wartość null. Bez takiego zachowania sprawdzanie wyniku zerowego z operatora [] wskazywałoby albo wartość zerową, albo brak klucza wejściowego, co nie jest dobre.


Dodatkowe informacje można znaleźć na MSDN: msdn.microsoft.com/en-gb/library/9tee9ht2.aspx
cyberzed

10

Jeśli sprawdzasz tylko przed próbą dodania nowej wartości, użyj ContainsKeymetody:

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

Jeśli sprawdzasz, czy wartość istnieje, użyj TryGetValuemetody opisanej w odpowiedzi Jona Skeeta.


8
TryGet jest lepszy
Ruben Bartelink

2
Ponieważ dwukrotnie rozwiązujesz wyszukiwanie klucza za pomocą tablicy hashy, jeśli natychmiast przejdziesz do zawartości. Wintellect PowerCollections ma również GetValueElseAddmetody, którym podajesz wartość (lub a Func<TValue>), aby również zapisać rozdzielczość we wkładce, jeśli zamierzasz dodać, jeśli jej tam nie ma. Domyślam się, że powodem, dla którego nie trafił do bibliotek .NET, jest to, że ścieżka dodawania jest rzadsza, jeśli używasz jej w stylu pamięci podręcznej]
Ruben Bartelink

@rub: Myślę, że to zależy od celu kodu. Jeśli chcesz użyć wartości, zgadzam się, że TryGetValuebyłoby lepiej, ale jeśli chcesz sprawdzić, czy słownik zawiera klucz, aby uniknąć podwójnych dodatków, powiedziałbym, że ContainsKeyjest równie dobry (jeśli nie lepszy).
Fredrik Mörk

@Fredrik: Jeśli chcesz tylko sprawdzić szczelność, to tak, warto użyć ContainsKey. Zauważ, że tak nie jest w przykładowym kodzie tej odpowiedzi.
Jon Skeet

@Jon: prawda, tak naprawdę brakowało mi, że wartość dodana została pobrana natychmiast po jej dodaniu.
Fredrik Mörk

3

Powinieneś sprawdzić Dictionary.ContainsKey (int key) przed próbą wyciągnięcia wartości.

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
Dlaczego chcesz, aby wyszukiwanie było wykonywane dwukrotnie?
Jon Skeet

2
@mookid: Moim zdaniem nie. Chodzi o to, aby spróbować znaleźć klucz i podjąć jedno działanie, jeśli zostanie znaleziony, a drugie, jeśli jest inaczej, prawda?
Jon Skeet

3
@Jon - Szczerze? Ponieważ nie wiedziałem o TryGetValue. Na szczęście teraz tak, więc będę wiedział w przyszłości. Mam zamiar pozostawić tę odpowiedź bez zmian, chociaż dyskusja jest cenna.
ZombieSheep

@Jon Skeet - Dlatego tu jestem. :)
ZombieSheep

@JonSkeet Ponieważ przed C # 7 nie można było używać TryGetValuew wyrażeniu lambda. Chociaż to sprawia, że ​​myślę, że nowe rozszerzenie C # byłoby catchoperatorem podobnym do nulloperatora koalescencji.
NetMage

1

Klasa pomocnicza jest przydatna:

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

Czasami zastanawiam się, dlaczego nie jest to dodane do standardowej biblioteki. Prawie wszystkie języki używające hashmaps zwracają wartość null, jeśli nie ma wpisu, a nie dziwny wyjątek. Element, którego nie ma w Twoim słowniku, nie jest wyjątkowym zachowaniem.
Adam Hess

@AdamHess - dlatego masz Hashtable () w c # ... niestety klucze są tam zapakowane ... :(
veljkoz


0

Powinieneś prawdopodobnie użyć:

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

Powodem, dla którego nie możesz sprawdzić null, jest to, że klucz tutaj jest typem wartości.


1
Typ wartości jest nieco nieistotny, ponieważ sprawdzenie wartości null nie przyniosłoby pożądanego efektu.
Jon Skeet

@Johannes, rozwiązanie Jona jest oczywiście o wiele lepsze, ale pytający stwierdził, że sprawdził, czy klucz istnieje i jest to Dictionary <int, int>, więc klucz jest tutaj również typem wartości.
Razzie

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

Jeśli klucz jest obecny w słowniku, zwraca wartość klucza, w przeciwnym razie zwraca 0.

Mam nadzieję, że ten kod ci pomoże.


1
Jeśli klucz istnieje, kod ten dwukrotnie wyszuka. TryGetValuewystarczy, użyj valuezamiastresult
Mathieu VIALES

0

Rozważ opcję hermetyzacji tego konkretnego słownika i podaj metodę zwracania wartości dla tego klucza:

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

Następnie możesz zarządzać zachowaniem tego słownika.

Na przykład tutaj: jeśli słownik nie ma klucza, zwraca klucz, który przekazujesz przez parametr.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.