¿Por qué es más rápido comprobar si el diccionario contiene la clave, en lugar de detectar la excepción en caso de que no la contenga?

Resuelto Petr asked hace 11 años • 2 respuestas

Imagina el código:

public class obj
{
    // elided
}

public static Dictionary<string, obj> dict = new Dictionary<string, obj>();

Método 1

public static obj FromDict1(string name)
{
    if (dict.ContainsKey(name))
    {
        return dict[name];
    }
    return null;
}

Método 2

public static obj FromDict2(string name)
{
    try
    {
        return dict[name];
    }
    catch (KeyNotFoundException)
    {
        return null;
    }
}

Tenía curiosidad por saber si hay una diferencia en el rendimiento de estas 2 funciones, porque la primera DEBE ser MÁS LENTA que la segunda, dado que necesita verificar dos veces si el diccionario contiene un valor, mientras que la segunda función solo necesita acceder al diccionario. una vez pero WOW, en realidad es lo contrario:

Bucle para 1.000.000 valores (con 100.000 existentes y 900.000 no existentes):

primera función: 306 milisegundos

segunda función: 20483 milisegundos

¿Porqué es eso?

EDITAR: Como puede observar en los comentarios debajo de esta pregunta, el rendimiento de la segunda función es en realidad ligeramente mejor que el de la primera en caso de que no existan 0 claves. Pero una vez que hay al menos 1 o más claves que no existen, el rendimiento de la segunda disminuye rápidamente.

Petr avatar Apr 19 '13 16:04 Petr
Aceptado

Por un lado, lanzar excepciones es inherentemente costoso , porque la pila debe desenrollarse, etc.
Por otro lado, acceder a un valor en un diccionario mediante su clave es barato, porque es una operación O(1) rápida.

Por cierto: la forma correcta de hacer esto es usarTryGetValue

obj item;
if(!dict.TryGetValue(name, out item))
    return null;
return item;

Esto accede al diccionario solo una vez en lugar de dos.
Si realmente desea regresar nullsi la clave no existe, el código anterior se puede simplificar aún más:

obj item;
dict.TryGetValue(name, out item);
return item;

Esto funciona porque TryGetValuese establece itemsi nullno existe ninguna clave name.

Daniel Hilgarth avatar Apr 19 '2013 09:04 Daniel Hilgarth

Los diccionarios están diseñados específicamente para realizar búsquedas de claves súper rápidas. Se implementan como tablas hash y cuantas más entradas, más rápidos son en relación con otros métodos. Se supone que el uso del motor de excepciones solo debe realizarse cuando su método no logró hacer lo que usted diseñó porque es un gran conjunto de objetos que le brinda mucha funcionalidad para manejar errores. Una vez construí una clase de biblioteca completa con todo rodeado por bloques try catch y me horroricé al ver el resultado de depuración que contenía una línea separada para cada una de las más de 600 excepciones.

Ed Hermanson avatar Apr 23 '2013 22:04 Ed Hermanson