Da entrada do MSDN no método Dictionary.TryGetValue :
Este método combina a funcionalidade do método ContainsKey e a propriedade Item.
Se a chave não for encontrada, o parâmetro value obterá o valor padrão apropriado para o tipo de valor TValue; por exemplo, 0 (zero) para tipos inteiros, falso para tipos booleanos e nulo para tipos de referência.
Use o método TryGetValue se o seu código tentar acessar frequentemente chaves que não estão no dicionário. O uso desse método é mais eficiente do que capturar a KeyNotFoundException lançada pela propriedade Item.
Este método aborda uma operação O (1).
Na descrição, não está claro se é mais eficiente ou apenas mais conveniente do que chamar ContainsKey e, em seguida, fazer a pesquisa. A implementação deTryGetValue
apenas chama ContainsKey e, em seguida, Item ou é realmente mais eficiente do que isso, fazendo uma única pesquisa?
Em outras palavras, o que é mais eficiente (ou seja, qual deles realiza menos pesquisas):
Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
ival = dict[ikey];
}
else
{
ival = default(int);
}
ou
Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);
Nota: Não estou procurando uma referência!
fonte
if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);
. Mas acho queTryGetValue
ainda é mais eficiente, pois o get e o conjunto da propriedade do indexador são usados, não é?Uma referência rápida mostra que
TryGetValue
tem uma ligeira vantagem:Isso produz
tornando o
ContainsKey + Item
acesso cerca de 40% mais lento, assumindo uma mistura uniforme de acertos e erros.Além disso, quando mudo o programa para sempre errar (ou seja, sempre olhando para cima
"b"
), as duas versões se tornam igualmente rápidas:Quando eu faço "todos os hits", no entanto,
TryGetValue
continua sendo um vencedor claro:fonte
TryGetValue
deve estar muito à frente. Além disso ... um nitpick ...DateTime
não é a melhor maneira de capturar medições de desempenho.TryGetValue
fica ainda mais na liderança. Editei a resposta para incluir os cenários "todos os hits" e "todos os erros".Any
- Como esta:Any(i=>i.Key==key)
. Nesse caso, sim, é uma pesquisa linear ruim do dicionário.DateTime.Now
será preciso apenas para alguns ms.Stopwatch
EmSystem.Diagnostics
vez disso, use a classe (que usa QueryPerformanceCounter sob as cobertas para fornecer uma precisão muito maior). Também é mais fácil de usar.Como nenhuma das respostas até agora realmente responde à pergunta, aqui está uma resposta aceitável que encontrei após algumas pesquisas:
Se você descompilar o TryGetValue, verá que está fazendo o seguinte:
enquanto o método ContainsKey é:
portanto, TryGetValue é apenas ContainsKey mais uma pesquisa de matriz se o item estiver presente.
Fonte
Parece que TryGetValue será quase duas vezes mais rápido que a combinação ContainsKey + Item.
fonte
Quem se importa :-)
Você provavelmente está perguntando porque
TryGetValue
é um problema usar - então encapsule-o assim com um método de extensão.Em seguida, basta ligar para:
ou
fonte
this
mas acontece que meu método foi duplicado duas vezes na minha base de código - uma vez com e um sem othis
é por isso que nunca o peguei! obrigado por consertar!TryGetValue
atribui um valor padrão ao parâmetro out value se a chave não existir, portanto, isso pode ser simplificado.if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
Por que você não testa?
Mas tenho certeza de que
TryGetValue
é mais rápido, porque faz apenas uma pesquisa. Obviamente, isso não é garantido, ou seja, implementações diferentes podem ter características de desempenho diferentes.A maneira como eu implementaria um dicionário é criando uma
Find
função interna que encontre o espaço para um item e depois construa o restante.fonte
Todas as respostas até agora, apesar de boas, perdem um ponto vital.
Os métodos nas classes de uma API (por exemplo, a estrutura .NET) fazem parte de uma definição de interface (não uma interface C # ou VB, mas uma interface no significado de ciência da computação).
Como tal, geralmente é incorreto perguntar se a chamada de um método desse tipo é mais rápida, a menos que a velocidade faça parte da definição formal da interface (o que não ocorre nesse caso).
Tradicionalmente, esse tipo de atalho (combinando pesquisa e recuperação) é mais eficiente, independentemente do idioma, infraestrutura, sistema operacional, plataforma ou arquitetura da máquina. Também é mais legível, porque expressa sua intenção explicitamente, em vez de implicar (a partir da estrutura do seu código).
Portanto, a resposta (de um hack antigo mal-humorado) é definitivamente 'Sim' (TryGetValue é preferível a uma combinação de ContainsKey e Item [Get] para recuperar um valor de um Dicionário).
Se você acha isso estranho, pense assim: Mesmo que as implementações atuais de TryGetValue, ContainsKey e Item [Get] não produzam nenhuma diferença de velocidade, você pode assumir que é provável que uma implementação futura (por exemplo, .NET v5) fará (TryGetValue será mais rápido). Pense na vida útil do seu software.
Como um aparte, é interessante notar que as tecnologias de definição de interface modernas típicas ainda raramente fornecem meios de definir formalmente as restrições de tempo. Talvez .NET v5?
fonte
Fazendo um programa de teste rápido, há definitivamente uma melhoria usando o TryGetValue com 1 milhão de itens em um dicionário.
Resultados:
ContainsKey + Item for 1000000 hits: 45ms
TryGetValue para 1000000 hits: 26ms
Aqui está o aplicativo de teste:
fonte
Na minha máquina, com cargas de RAM, quando executado no modo RELEASE (não DEBUG),
ContainsKey
é igual aTryGetValue
/try-catch
se todas as entradas noDictionary<>
forem encontradas.ContainsKey
supera todos eles de longe quando há apenas algumas entradas de dicionário não encontradas (no meu exemplo abaixo, definaMAXVAL
como algo maior doENTRIES
que ter algumas entradas perdidas):Resultados:
Aqui está o meu código:
fonte
Além de projetar uma marca de microbench que fornecerá resultados precisos em uma configuração prática, você pode inspecionar a fonte de referência do .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 eles chamam o
FindEntry(TKey)
método que faz a maior parte do trabalho e não memoriza seu resultado, portanto, a chamadaTryGetValue
é quase duas vezes mais rápida queContainsKey
+Item
.A interface inconveniente de
TryGetValue
pode ser adaptada usando um método de extensão :Desde o C # 7.1, você pode substituir
default(TValue)
por simplesdefault
. O tipo é inferido.Uso:
Ele retorna
null
para tipos de referência cuja pesquisa falha, a menos que um valor padrão explícito seja especificado.fonte
Se você estiver tentando obter o valor do dicionário, o TryGetValue (valor da chave, saída) é a melhor opção, mas se você estiver verificando a presença da chave, uma nova inserção, sem substituir as chaves antigas, e somente com esse escopo, ContainsKey (key) é a melhor opção, o benchmark pode confirmar isso:
Este é um exemplo verdadeiro. Eu tenho um serviço que, para cada "Item" criado, associa um número progressivo, esse número, toda vez que você cria um novo item, deve ser encontrado gratuitamente. Se você excluir um Item, o número gratuito se tornará grátis, é claro que isso não é otimizado, pois eu tenho uma var estática que armazena em cache o número atual, mas, se você terminar todos os números, poderá reiniciar de 0 a UInt32.MaxValue
Teste executado:
Adicionando elementos
na tabela de hash ... Feito em 0,5908 - pausa ....
Adicionando elementos no dicionário ...
Feito em 0,2679 - pausa ....
Encontrando o primeiro número livre para inserção
Primeiro método : ContainsKey
Concluído em 0,0561 - valor adicionado 1000000 no dicionário - pausa ....
Segundo método: TryGetValue
Concluído em 0,0643 - valor adicionado 1000001 no dicionário - pausa ...
Teste de hashtable
Concluído em 0, 3015 - valor adicionado 1000000 na hashtable - pausa ....
Pressione qualquer tecla para continuar. .
Se alguns de vocês perguntarem se as ContainsKeys podem ter uma vantagem, tentei inverter a chave TryGetValue com Contains, o resultado é o mesmo.
Então, para mim, com uma consideração final, tudo depende da maneira como o programa se comporta.
fonte