Diferentes maneiras de adicionar ao Dicionário

107

Qual é a diferença em Dictionary.add(key, value)e Dictionary[key] = value?

Percebi que a última versão não lança um ArgumentExceptionao inserir uma chave duplicada, mas há algum motivo para preferir a primeira versão?

Edit : Alguém tem uma fonte confiável de informações sobre isso? Eu tentei o MSDN, mas é como sempre uma caça ao ganso selvagem :(

Sune Rievers
fonte

Respostas:

109

O desempenho é quase 100% idêntico. Você pode verificar isso abrindo a classe no Reflector.net

Este é o Este indexador:

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

E este é o método Add:

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

Não vou postar todo o método Insert porque é bastante longo, no entanto, a declaração do método é esta:

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

E mais abaixo na função, isso acontece:

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

Que verifica se a chave já existe, e se existe e o parâmetro add é verdadeiro, ele lança a exceção.

Portanto, para todos os fins e intenções, o desempenho é o mesmo.

Como algumas outras menções, trata-se de saber se você precisa da verificação, para tentar adicionar a mesma chave duas vezes.

Desculpe pela postagem longa, espero que esteja tudo bem.

Steffen
fonte
+1 Muito interessante, obrigado pela sua postagem! Parece que o desempenho é quase idêntico aqui, como os outros pôsteres sugeriram, de qualquer forma ótimo achado :)
Sune Rievers
70

A primeira versão adicionará um novo KeyValuePair ao dicionário, lançando se a chave já estiver no dicionário. O segundo, usando o indexador, adicionará um novo par se a chave não existir, mas sobrescreverá o valor da chave se ela já existir no dicionário.

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
fonte
+1 Você tem uma fonte para as informações acima? Estou interessado em saber se há algum efeito colateral ou advertência no uso da primeira ou da última forma.
Sune Rievers
3
Não tenho realmente uma fonte como tal, apenas de cara, mas não acho que haja muito mais do que mencionado nos outros comentários. Se bem me lembro, Add simplesmente usa o indexador, mas verifica primeiro se a chave já está em uso.
hhravn
1
Alteração da resposta aceita para Steffen, porque sua documentação é de primeira qualidade. Esta ainda é uma ótima resposta.
Sune Rievers
@Sune: Má jogada ... pessoalmente, acho que essa resposta está muito à frente da de Steffen ... direto ao ponto e um exemplo decente.
demoncodemonkey
1
@SuneRievers Eu acho que será mais útil para as pessoas, se for aceita uma resposta para isso, ao invés da resposta aceita atualmente. Porque isso responde exatamente à pergunta ("qual é a diferença"), ao invés da pergunta aceita (que fala sobre desempenho, e quase 99% dos usuários que pesquisam esse tópico (como eu), precisavam da "diferença" (por que encontrei isso tópico), e a resposta aceita foi inútil para mim e desperdiçou a segunda. Em vez disso, esta resposta é exata.
T.Todua
30

Dictionary.Add(key, value)e Dictionary[key] = valuetêm finalidades diferentes:

  • Use o Addmétodo para adicionar um novo par de chave / valor, as chaves existentes não serão substituídas (um ArgumentExceptioné lançado).
  • Use o indexador se você não se importa se a chave já existe no dicionário, em outras palavras: adicione o par chave / valor se a chave não estiver no dicionário ou substitua o valor pela chave especificada se a chave já estiver no dicionário.
Michael Damatov
fonte
1
O código que descreve a intenção é importante e extremamente valioso (e requer poucos ou nenhum comentário). Essa resposta mostra a diferença de intenção com os dois métodos e deve ser seguida ao escolher um. Em outras palavras, não use a 'adição do indexador' quando você sabe com antecedência que sempre adicionará, ou mesmo que sempre deve adicionar. Ter uma exceção IndexOutOfBounds lançada é melhor do que um comportamento inesperado.
Ryancdotnet
28

Para responder à pergunta, primeiro precisamos dar uma olhada no propósito de um dicionário e da tecnologia subjacente.

Dictionaryé a lista de KeyValuePair<Tkey, Tvalue>onde cada valor é representado por sua chave exclusiva. Digamos que temos uma lista de suas comidas favoritas. Cada valor (nome do alimento) é representado por sua chave exclusiva (a posição = o quanto você gosta desse alimento).

Código de exemplo:

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

Digamos que você queira se manter saudável, mudou de ideia e deseja substituir seu "hambúrguer" favorito por salada. Sua lista ainda é uma lista de seus favoritos, você não mudará a natureza da lista. Seu favorito permanecerá em primeiro lugar na lista, apenas seu valor será alterado. É quando você chama isso de:

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

Mas não se esqueça que você é o programador, e a partir de agora você termina suas frases com; você se recusa a usar emojis porque eles gerariam um erro de compilação e toda a lista de favoritos é baseada em 0 índice.

Sua dieta também mudou! Então, você altera sua lista novamente:

/*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");

Existem duas possibilidades com a definição: você deseja fornecer uma nova definição para algo que não existia antes ou deseja alterar a definição que já existe.

O método Adicionar permite adicionar um registro, mas apenas sob uma condição: a chave para esta definição pode não existir no seu dicionário.

Agora vamos dar uma olhada sob o capô. Quando você está fazendo um dicionário, seu compilador faz uma reserva para o intervalo (espaços na memória para armazenar seus registros). Bucket não armazena chaves da maneira como você as define. Cada chave é hash antes de ir para o balde (definido pela Microsoft), vale a pena mencionar que a parte do valor permanece inalterada.

Usarei o algoritmo de hash CRC32 para simplificar meu exemplo. Quando você define:

myDietFavorites[0] = "Pizza";

O que vai para o balde é db2dc565 "Pizza" (simplificado).

Quando você altera o valor com:

myDietFavorites[0] = "Spaghetti";

Você hash seu 0, que é novamente db2dc565, e então procura esse valor em seu depósito para descobrir se ele está lá. Se estiver lá, você simplesmente reescreve o valor atribuído à chave. Se não estiver lá, você colocará seu valor no balde.

Ao chamar a função Adicionar em seu dicionário, como:

myDietFavorite.Add(0, "Chocolate");

Você hash seu 0 para comparar seu valor com os do intervalo. Você pode colocá-lo no balde somente se ele não estiver lá .

É crucial saber como funciona, especialmente se você trabalha com dicionários de string ou tipo de chave char. É sensível a maiúsculas e minúsculas devido ao hashing em curso. Por exemplo, "nome"! = "Nome". Vamos usar nosso CRC32 para representar isso.

O valor para "nome" é: e04112b1 O valor para "Nome" é: 1107fb5b

Kamil Kurzynowski
fonte
Essas duas linhas são suficientes para entender ....... Existem duas possibilidades com a definição, você deseja fornecer uma nova definição para algo que não existia antes ou deseja alterar a definição que já existe. O método Adicionar permite adicionar um registro, mas apenas sob uma condição: a chave para esta definição pode não existir no seu dicionário.
Niraj Trivedi
4

Sim, essa é a diferença, o método Add lança uma exceção se a chave já existir.

A razão para usar o método Add é exatamente esta. Se o dicionário ainda não deve conter a chave, você geralmente deseja a exceção para que esteja ciente do problema.

Guffa
fonte
0

Dadas as semelhanças mais do que prováveis ​​no desempenho, use o que parecer mais correto e legível para o trecho de código que você está usando.

Sinto que uma operação que descreve uma adição, sendo a presença da chave já uma exceção realmente rara, é melhor representada com a adição. Semanticamente, faz mais sentido.

O dict[key] = valuerepresenta melhor uma substituição. Se eu vir esse código, eu meio que espero que a chave já esteja no dicionário de qualquer maneira.

Jorge Córdoba
fonte
Eu presumiria que há um pequeno ganho de desempenho por não verificar se a chave existe primeiro. Eu não esperava dic[key] = valueque a chave já estivesse presente, mas acho que isso é discutível;)
Sune Rievers
2
+ Acho que jogar nunca deve ser usado como forma de verificar se a chave já está representada. if (! strings.ContainsKey ("foo")) strings.Add ("foo", "bar");
hhravn
0

Um está atribuindo um valor, enquanto o outro está adicionando ao Dicionário uma nova Chave e Valor.

Joshua Smith
fonte
0

Para inserir o valor no dicionário

 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
fonte