Diferentes formas de agregar contenido al diccionario

107

¿Cuál es la diferencia en Dictionary.add(key, value)y Dictionary[key] = value?

He notado que la última versión no arroja un ArgumentExceptionerror al insertar una clave duplicada, pero ¿hay alguna razón para preferir la primera versión?

Editar : ¿Alguien tiene una fuente autorizada de información sobre esto? Probé MSDN, pero como siempre es una búsqueda inútil :(

Sune Rievers
fuente

Respuestas:

109

El rendimiento es casi un 100% idéntico. Puede comprobar esto abriendo la clase en Reflector.net

Este es el indexador This:

public TValue this[TKey key]
{
    get
    {
        int index = this.FindEntry(key);
        if (index >= 0)
        {
            return this.entries[index].value;
        }
        ThrowHelper.ThrowKeyNotFoundException();
        return default(TValue);
    }
    set
    {
        this.Insert(key, value, false);
    }
}

Y este es el método Add:

public void Add(TKey key, TValue value)
{
    this.Insert(key, value, true);
}

No publicaré el método Insert completo ya que es bastante largo, sin embargo, la declaración del método es la siguiente:

private void Insert(TKey key, TValue value, bool add)

Y más abajo en la función, sucede esto:

if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key))
{
    if (add)
    {
        ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
    }

Lo que comprueba si la clave ya existe, y si existe y el parámetro add es verdadero, lanza la excepción.

Entonces, para todos los propósitos e intenciones, el rendimiento es el mismo.

Como algunas otras menciones, se trata de si necesita el cheque, para intentar agregar la misma clave dos veces.

Perdón por la publicación tan larga, espero que esté bien.

Steffen
fuente
+1 Muy interesante, ¡gracias por tu publicación! Parece que el rendimiento es casi idéntico aquí, como han insinuado los otros carteles, de todos modos un gran hallazgo :)
Sune Rievers
70

La primera versión agregará un nuevo KeyValuePair al diccionario, arrojando si la clave ya está en el diccionario. El segundo, usando el indexador, agregará un nuevo par si la clave no existe, pero sobrescribirá el valor de la clave si ya existe en el diccionario.

IDictionary<string, string> strings = new Dictionary<string, string>();

strings["foo"] = "bar";          //strings["foo"] == "bar"
strings["foo"] = string.Empty;   //strings["foo"] == string.empty
strings.Add("foo", "bar");       //throws     
hhravn
fuente
+1 ¿Tiene una fuente para la información anterior? Estoy interesado en saber si hay efectos secundarios o advertencias al usar la primera o la última forma.
Sune Rievers
3
Realmente no tengo una fuente como tal, simplemente fuera de mi cabeza, pero no creo que haya mucho más que lo mencionado en los otros comentarios. Si recuerdo correctamente, Add simplemente usa el indexador, pero primero verifica si la clave ya está en uso.
hhravn
1
Se cambió la respuesta aceptada a la de Steffen, porque su documentación es de primera categoría. Sin embargo, esta sigue siendo una gran respuesta.
Sune Rievers
@Sune: Mal movimiento ... personalmente, creo que esta respuesta está muy por delante de la de Steffen ... directo al grano y un ejemplo decente.
demoncodemonkey
1
@SuneRievers Creo que será más útil para las personas, si se acepta la respuesta para esto, en lugar de la respuesta aceptada actual. Porque esto responde exactamente a la pregunta ("cuál es la diferencia"), en lugar de la aceptada (que habla sobre el rendimiento, y casi el 99% de los usuarios que buscan este tema (como yo), necesitaban la "diferencia" (por qué me encontré con esto tema), y la respuesta aceptada era inútil para mí y desechos nuestro segundo lugar, esta respuesta es exacta..
T.Todua
30

Dictionary.Add(key, value)y Dictionary[key] = valuetienen diferentes propósitos:

  • Use el Addmétodo para agregar un nuevo par clave / valor, las claves existentes no serán reemplazadas ( ArgumentExceptionse lanza una).
  • Utilice el indexador si no le importa si la clave ya existe en el diccionario, en otras palabras: agregue el par clave / valor si la clave no está en el diccionario o reemplace el valor de la clave especificada si la clave ya está en el diccionario.
Michael Damatov
fuente
1
El código que autodescribe la intención es importante e inmensamente valioso (y requiere pocos o ningún comentario). Esta respuesta muestra la diferencia en la intención con ambos métodos y debe seguirse al elegir uno. En otras palabras, no use el 'indexador agregar' cuando sepa de antemano que siempre agregará, o incluso que siempre debe agregar. Tener una excepción IndexOutOfBounds es mejor que un comportamiento inesperado.
ryancdotnet
28

Para responder a la pregunta, primero debemos echar un vistazo al propósito de un diccionario y la tecnología subyacente.

Dictionary es la lista de KeyValuePair<Tkey, Tvalue> donde cada valor está representado por su clave única. Digamos que tenemos una lista de sus comidas favoritas. Cada valor (nombre del alimento) está representado por su clave única (una posición = cuánto le gusta este alimento).

Código de ejemplo:

Dictionary<int, string> myDietFavorites = new Dictionary<int, string>()
{
    { 1, "Burger"},
    { 2, "Fries"},
    { 3, "Donuts"}
};

Digamos que desea mantenerse saludable, ha cambiado de opinión y desea reemplazar su "hamburguesa" favorita con ensalada. Su lista sigue siendo una lista de sus favoritos, no cambiará la naturaleza de la lista. Su favorito seguirá siendo el número uno en la lista, solo cambiará su valor. Aquí es cuando llamas a esto:

/*your key stays 1, you only replace the value assigned to this key
  you alter existing record in your dictionary*/
myDietFavorites[1] = "Salad";

Pero no olvides que eres el programador ya partir de ahora terminas tus frases con; te niegas a usar emojis porque arrojarían un error de compilación y toda la lista de favoritos se basa en un índice 0.

¡Tu dieta también cambió! Entonces modificas tu lista de nuevo:

/*you don't want to replace Salad, you want to add this new fancy 0
  position to your list. It wasn't there before so you can either define it*/
myDietFavorites[0] = "Pizza";

/*or Add it*/
myDietFavorites.Add(0, "Pizza");

Hay dos posibilidades con la definición, o desea dar una nueva definición para algo que no existía antes o desea cambiar la definición que ya existe.

El método Agregar le permite agregar un registro, pero solo bajo una condición: la clave para esta definición puede no existir en su diccionario.

Ahora vamos a mirar debajo del capó. Cuando está haciendo un diccionario, su compilador hace una reserva para el depósito (espacios en la memoria para almacenar sus registros). Bucket no almacena las claves de la forma en que las define. Cada clave tiene un hash antes de ir al depósito (definido por Microsoft), vale la pena mencionar que la parte del valor permanece sin cambios.

Usaré el algoritmo hash CRC32 para simplificar mi ejemplo. Al definir:

myDietFavorites[0] = "Pizza";

Lo que va al cubo es db2dc565 "Pizza" (simplificado).

Cuando altera el valor con:

myDietFavorites[0] = "Spaghetti";

Pones tu 0 que es de nuevo db2dc565 luego buscas este valor en tu depósito para encontrar si está allí. Si está allí, simplemente vuelva a escribir el valor asignado a la clave. Si no está allí, pondrá su valor en el cubo.

Cuando llama a la función Agregar en su diccionario como:

myDietFavorite.Add(0, "Chocolate");

Pones un hash en tu 0 para comparar su valor con los del cubo. Puede colocarlo en el balde solo si no está allí .

Es crucial saber cómo funciona, especialmente si trabaja con diccionarios de clave de tipo cadena o char. Es sensible a mayúsculas y minúsculas debido a que se somete a hash. Por ejemplo, "nombre"! = "Nombre". Usemos nuestro CRC32 para representar esto.

El valor de "nombre" es: e04112b1 El valor de "Nombre" es: 1107fb5b

Kamil Kurzynowski
fuente
Estas dos líneas son suficientes para entender ... Hay dos posibilidades con la definición, o desea dar una nueva definición para algo que no existía antes o desea cambiar la definición que ya existe. El método Agregar le permite agregar un registro, pero solo bajo una condición: la clave para esta definición puede no existir en su diccionario.
Niraj Trivedi
4

Sí, esa es la diferencia, el método Add lanza una excepción si la clave ya existe.

La razón para usar el método Add es exactamente esta. Si se supone que el diccionario no contiene ya la clave, normalmente querrá la excepción para que se dé cuenta del problema.

Guffa
fuente
0

Dadas las similitudes más que probables en el rendimiento, use lo que se sienta más correcto y legible para el fragmento de código que está usando.

Siento una operación que describe una adición, siendo la presencia de la clave una excepción realmente rara que se representa mejor con la adición. Semánticamente tiene más sentido.

El dict[key] = valuerepresenta mejor una sustitución. Si veo ese código, espero que la clave ya esté en el diccionario de todos modos.

Jorge Córdoba
fuente
Asumiría que hay una pequeña ganancia de rendimiento al no verificar si la clave existe primero. No esperaría dic[key] = valueque la clave ya estuviera presente, pero supongo que es discutible;)
Sune Rievers
2
+ Creo que el lanzamiento nunca debe usarse como una forma de verificar si la clave ya está representada. if (! strings.ContainsKey ("foo")) strings.Add ("foo", "bar");
hhravn
0

Uno asigna un valor mientras que el otro agrega al Diccionario una nueva Clave y Valor.

Joshua Smith
fuente
0

Para insertar el valor en el diccionario

 Dictionary<string, string> dDS1 = new Dictionary<string, string>();//Declaration
 dDS1.Add("VEqpt", "aaaa");//adding key and value into the dictionary
 string Count = dDS1["VEqpt"];//assigning the value of dictionary key to Count variable
 dDS1["VEqpt"] = Count + "bbbb";//assigning the value to key
Maghalakshmi Saravana
fuente