¿Qué sucede con la búsqueda del diccionario C # <int, int> si la clave no existe?

121

Intenté comprobar si era nulo, pero el compilador advierte que esta condición nunca ocurrirá. ¿Qué debería estar buscando?

deltanovember
fuente

Respuestas:

196

Suponiendo que se desea obtener el valor si la tecla hace existir, su uso 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
}

(Usar ContainsKeyy luego el indexador hace que busque la clave dos veces, lo cual es bastante inútil).

Tenga en cuenta que incluso si estuviera usando tipos de referencia, la verificación de nulos no funcionaría: el indexador Dictionary<,>generará una excepción si solicita una clave que falta, en lugar de devolver un valor nulo. (Esta es una gran diferencia entre Dictionary<,>y Hashtable.)

Jon Skeet
fuente
@JonSkeet ¿No está TryGetValue haciendo una doble búsqueda también ( como se indica en el cuerpo de esta pregunta )?
nawfal
5
@nawfal: No veo ninguna indicación de que esa pregunta indique eso en absoluto. Dice que está haciendo más trabajo que ContainsKey, lo cual es cierto, porque también tiene que extraer el valor. Sin embargo, no está haciendo dos búsquedas.
Jon Skeet
Ingenuamente, seguí esperando nulo, pero para Dictionary <TKey, enum>, esto devuelve el equivalente "0" en la enumeración.
Jess
23

El Diccionario lanza una KeyNotFoundexcepción en caso de que el diccionario no contenga su clave.

Como se sugiere, ContainsKeyes la precaución adecuada. TryGetValuetambién es eficaz.

Esto permite que el diccionario almacene un valor nulo de manera más efectiva. Sin que se comporte de esta manera, la verificación de un resultado nulo del operador [] indicaría un valor nulo O la inexistencia de la clave de entrada, lo cual no es bueno.

antik
fuente
Se puede encontrar información adicional en MSDN: msdn.microsoft.com/en-gb/library/9tee9ht2.aspx
cyberzed
10

Si solo está verificando antes de intentar agregar un nuevo valor, use el ContainsKeymétodo:

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

Si está comprobando que existe el valor, use el TryGetValuemétodo como se describe en la respuesta de Jon Skeet.

ChrisF
fuente
8
TryGet es mejor
Ruben Bartelink
2
Porque está resolviendo la búsqueda de claves a través de la tabla hash dos veces si obtiene inmediatamente después de Contiene. Wintellect PowerCollections también tiene GetValueElseAddmétodos a los que le da un valor (o a Func<TValue>) para guardar también la resolución en el Insertar si va a agregar si no está allí. Supongo que la razón por la que no se ha incluido en las bibliotecas de .NET es porque la ruta Add es menos frecuente si la estás usando en un estilo de caché]
Ruben Bartelink
@rub: Supongo que eso depende del propósito del código. Si desea usar el valor, estoy de acuerdo en que TryGetValuesería mejor, pero si desea verificar si el diccionario contiene la clave para evitar adiciones duplicadas, diría que ContainsKeyes igual de bueno (si no mejor).
Fredrik Mörk
@Fredrik: Si solo desea hacer una verificación de contención, entonces sí, vale la pena usar ContainsKey. Tenga en cuenta que ese no es el caso en el código de muestra de esta respuesta.
Jon Skeet
@Jon: cierto, de hecho me perdí que el valor agregado se obtuvo inmediatamente después de que se agregó.
Fredrik Mörk
3

Debe buscar Dictionary.ContainsKey (clave int) antes de intentar extraer el valor.

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.
}
ZombieOvejas
fuente
2
¿Por qué quieres que haga la búsqueda dos veces?
Jon Skeet
2
@mookid: No en mi opinión. La idea es tratar de buscar la clave y tomar un curso de acción si se encuentra, y otro curso de acción de lo contrario, ¿verdad?
Jon Skeet
3
@Jon - ¿Honestamente? Porque yo no sabía nada TryGetValue. Afortunadamente, lo hago ahora, así que lo sabré en el futuro. Dejaré esta respuesta intacta, aunque la discusión es valiosa.
ZombieSheep
@Jon Skeet - Por eso estoy aquí. :)
ZombieSheep
@JonSkeet Porque antes de C # 7, no se podía usar TryGetValueen una expresión lambda. Aunque eso me hace pensar que una nueva extensión de C # sería un catchoperador similar al nulloperador de fusión.
NetMage
1

Una clase de ayudante es útil:

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;
    }
}
sheamus
fuente
A veces me pregunto por qué esto no se agrega a la biblioteca estándar. Casi todos los idiomas que usan hashmaps devuelven un valor nulo si no hay una entrada, no es una maldita excepción. Un elemento que no existe en su diccionario no es un comportamiento excepcional.
Adam Hess
@AdamHess: es por eso que tiene Hashtable () en c # ... desafortunadamente, sus claves se empaquetan allí ... :(
veljkoz
0

Probablemente deberías usar:

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

La razón por la que no puede verificar si es nulo es que la clave aquí es un tipo de valor.

Razzie
fuente
1
El tipo de valor es algo irrelevante, ya que la comprobación de nulo no tendría el efecto deseado.
Jon Skeet
@Johannes, la solución de Jon es, por supuesto, mucho mejor, pero el autor de la pregunta indicó que verificó si la clave existe, y es un Dictionary <int, int>, por lo que la clave también es un tipo de valor aquí.
Razzie
0
int result= YourDictionaryName.TryGetValue(key, out int value) ? YourDictionaryName[key] : 0;

Si la clave está presente en el diccionario, devuelve el valor de la clave; de ​​lo contrario, devuelve 0.

Espero que este código te ayude.

Nitika Chopra
fuente
1
Si la clave existe, este código se buscará dos veces. TryGetValuees suficiente, use en valuelugar deresult
Mathieu VIALES
0

Considere la opción de encapsular este diccionario en particular y proporcione un método para devolver el valor de esa clave:

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

Entonces puede administrar el comportamiento de este diccionario.

Por ejemplo aquí: si el diccionario no tiene la clave, devuelve la clave que pasas por parámetro.

pablocom96
fuente