De la entrada de MSDN en Dictionary. TryGetValue Method :
Este método combina la funcionalidad del método ContainsKey y la propiedad Item.
Si no se encuentra la clave, entonces el parámetro de valor obtiene el valor predeterminado apropiado para el tipo de valor TValue; por ejemplo, 0 (cero) para tipos enteros, falso para tipos booleanos y nulo para tipos de referencia.
Use el método TryGetValue si su código intenta acceder con frecuencia a claves que no están en el diccionario. Usar este método es más eficiente que capturar la KeyNotFoundException generada por la propiedad Item.
Este método se acerca a una operación O (1).
De la descripción, no está claro si es más eficiente o simplemente más conveniente que llamar a ContainsKey y luego realizar la búsqueda. ¿La implementación de TryGetValue
simplemente llama a ContainsKey y luego a Item o es realmente más eficiente que eso al hacer una sola búsqueda?
En otras palabras, qué es más eficiente (es decir, cuál realiza menos búsquedas):
Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
ival = dict[ikey];
}
else
{
ival = default(int);
}
o
Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);
Nota: ¡No estoy buscando un punto de referencia!
fuente
if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);
. Pero creo que esoTryGetValue
es aún más eficiente ya que se usa el get y set de la propiedad indexadora, ¿no es así?Un punto de referencia rápido muestra que
TryGetValue
tiene una ligera ventaja:Esto produce
haciendo el
ContainsKey + Item
acceso un 40% más lento suponiendo una combinación uniforme de aciertos y errores.Además, cuando cambio el programa para que siempre pierda (es decir, siempre busco
"b"
) las dos versiones se vuelven igualmente rápidas:Sin embargo, cuando lo hago "todos los éxitos",
TryGetValue
sigue siendo un claro ganador:fuente
TryGetValue
debe estar muy por delante. Además ... un punto crítico ...DateTime
no es la mejor manera de capturar mediciones de rendimiento.TryGetValue
pone aún más en la delantera. Edité la respuesta para incluir los escenarios "todos los aciertos" y "todos los errores".Any
- De esta manera:Any(i=>i.Key==key)
. En cuyo caso, sí, esa es una mala búsqueda lineal del diccionario.DateTime.Now
solo será preciso a unos pocos ms. Utilice laStopwatch
clase en suSystem.Diagnostics
lugar (que utiliza QueryPerformanceCounter debajo de las cubiertas para proporcionar una precisión mucho mayor). También es más fácil de usar.Como ninguna de las respuestas hasta ahora responde realmente a la pregunta, aquí hay una respuesta aceptable que encontré después de algunas investigaciones:
Si descompila TryGetValue, verá que está haciendo esto:
mientras que el método ContainsKey es:
entonces TryGetValue es solo ContainsKey más una búsqueda de matriz si el elemento está presente.
Fuente
Parece que TryGetValue será casi el doble de rápido que la combinación ContainsKey + Item.
fuente
A quien le importa :-)
Probablemente esté preguntando porque
TryGetValue
es difícil de usar, así que encapsúlelo así con un método de extensión.Entonces solo llame:
o
fuente
this
pero resulta que tengo mi método duplicado dos veces en mi base de código, una vez con y una sin la, ¡this
por eso nunca lo entendí! gracias por arreglar!TryGetValue
asigna un valor predeterminado al parámetro de valor de salida si la clave no existe, por lo que esto podría simplificarse.if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
¿Por qué no lo pruebas?
Pero estoy bastante seguro de que
TryGetValue
es más rápido, porque solo hace una búsqueda. Por supuesto, esto no está garantizado, es decir, diferentes implementaciones pueden tener diferentes características de rendimiento.La forma en que implementaría un diccionario es creando una
Find
función interna que encuentre el espacio para un elemento, y luego construya el resto sobre eso.fuente
Todas las respuestas hasta ahora, aunque buenas, pierden un punto vital.
Los métodos en las clases de una API (por ejemplo, el marco .NET) forman parte de una definición de interfaz (no una interfaz C # o VB, sino una interfaz en el significado de la informática).
Como tal, generalmente es incorrecto preguntar si llamar a un método de este tipo es más rápido, a menos que la velocidad sea parte de la definición formal de la interfaz (que no es en este caso).
Tradicionalmente, este tipo de acceso directo (que combina búsqueda y recuperación) es más eficiente independientemente del idioma, la infraestructura, el sistema operativo, la plataforma o la arquitectura de la máquina. También es más legible, porque expresa su intención explícitamente, en lugar de implicarla (desde la estructura de su código).
Entonces, la respuesta (de un viejo truco canoso) es definitivamente 'Sí' (TryGetValue es preferible a una combinación de ContainsKey y Item [Get] para recuperar un valor de un Diccionario).
Si cree que esto suena extraño, piense así: incluso si las implementaciones actuales de TryGetValue, ContainsKey y Item [Get] no producen ninguna diferencia de velocidad, puede suponer que es probable que una implementación futura (por ejemplo .NET v5) hará (TryGetValue será más rápido). Piensa en la vida útil de tu software.
Por otro lado, es interesante observar que las tecnologías de definición de interfaz modernas típicas todavía rara vez proporcionan algún medio para definir formalmente las restricciones de tiempo. ¿Quizás .NET v5?
fuente
Al hacer un programa de prueba rápida, definitivamente hay una mejora usando TryGetValue con 1 millón de elementos en un diccionario.
Resultados:
Contiene Key + Item para 1000000 golpes: 45ms
TryGetValue para 1000000 visitas: 26 ms
Aquí está la aplicación de prueba:
fuente
En mi máquina, con mucha RAM, cuando se ejecuta en modo RELEASE (no DEBUG),
ContainsKey
es igualTryGetValue
/try-catch
si todas las entradas en elDictionary<>
.ContainsKey
los supera a todos con diferencia cuando solo se encuentran algunas entradas del diccionario (en mi ejemplo a continuación, establezcaMAXVAL
algo más grande queENTRIES
perder algunas entradas):Resultados:
Aquí está mi código:
fuente
Además de diseñar un microbenchmark que brinde resultados precisos en un entorno práctico, puede inspeccionar la fuente de referencia de .NET Framework.
System.Collections.Generic.Dictionary<TKey, TValue>.TryGetValue(TKey, out TValue)
System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey)
System.Collections.Generic.Dictionary<TKey, TValue>.Item(TKey)
Todos ellos llaman al
FindEntry(TKey)
método que hace la mayor parte del trabajo y no memoriza su resultado, por lo que llamarTryGetValue
es casi el doble de rápido queContainsKey
+Item
.La interfaz inconveniente de
TryGetValue
se puede adaptar usando un método de extensión :Desde C # 7.1, puede reemplazar
default(TValue)
con plaindefault
. El tipo se infiere.Uso:
Devuelve los
null
tipos de referencia cuya búsqueda falla, a menos que se especifique un valor predeterminado explícito.fuente
Si está tratando de obtener el valor del diccionario, TryGetValue (clave, valor de salida) es la mejor opción, pero si está buscando la presencia de la clave, para una nueva inserción, sin sobrescribir las claves antiguas, y solo con ese alcance, ContainsKey (clave) es la mejor opción, el punto de referencia puede confirmar esto:
Este es un verdadero ejemplo, tengo un servicio que para cada "Artículo" creado, asocia un número progresivo, este número, cada vez que crea un nuevo artículo, debe encontrarse libre, si elimina un Artículo, el número libre se convierte en gratis, por supuesto, esto no está optimizado, ya que tengo una var estática que almacena en caché el número actual, pero en caso de que finalice todos los números, puede volver a comenzar de 0 a UInt32.MaxValue
Prueba ejecutada:
Agregando elementos a la tabla hash ...
Hecho en 0,5908 - pausa ....
Agregando elementos al diccionario ...
Hecho en 0,2679 - pausa ....
Encontrando el primer número libre para inserción
Primer método : ContainsKey
Hecho en 0,0561 - valor agregado 1000000 en diccionario - pausa ....
Segundo método: TryGetValue
Hecho en 0,0643 - valor agregado 1000001 en diccionario - pausa ....
Prueba de tabla hash
Hecho en 0, 3015 - valor agregado 1000000 en tabla hash - pausa ....
Presione cualquier tecla para continuar. .
Si algunos de ustedes se preguntan si las ContainsKeys podrían tener una ventaja, incluso he intentado invertir el TryGetValue con la clave Contains, el resultado es el mismo.
Entonces, para mí, con una consideración final, todo depende de la forma en que se comporta el programa.
fuente