Dictionary vs Hashtable em C#: por que o dicionário sempre vence?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Dictionary vs Hashtable: batalha de coleções em C#

\n\n

Imagine a situação: você está escrevendo código em C# e precisa salvar e recuperar dados rapidamente por chave. Você abre a documentação e vê duas classes semelhantes — Dictionary e Hashtable. Ambas funcionam no princípio "chave-valor", ambas prometem acesso ultrarrápido aos dados. Mas qual escolher?

\n\n

Se você já se fez essa pergunta, não está sozinho. No Stack Overflow, o tópico "Why is Dictionary preferred over Hashtable in C#?" acumulou milhares de visualizações e centenas de votos. E hoje vamos resolver essa questão de uma vez por todas. Spoiler: Dictionary quase sempre vence. Mas vamos entender por quê.

\n\n

Uma breve viagem pela história: de onde veio o Hashtable?

\n\n

Para entender a essência do confronto, precisamos olhar para a história do C#. Quando a plataforma .NET foi lançada (versão 1.0), os desenvolvedores tinham acesso à classe Hashtable do namespace System.Collections. Era uma tabela hash clássica que podia armazenar qualquer objeto. Parece legal, certo? Mas havia um problema — falta de tipagem.

\n\n

Com o lançamento do .NET 2.0 e o surgimento dos genéricos (generics), a Microsoft apresentou uma nova coleção — Dictionary<TKey, TValue> do namespace System.Collections.Generic. E foi aí que a mágica começou.

\n\n

Segurança de tipos: a principal vantagem do Dictionary

\n\n

Vamos ver um exemplo específico de código. Veja como é trabalhar com Hashtable:

\n\n
// Jeito antigo: Hashtable\nHashtable hashtable = new Hashtable();\nhashtable.Add("chave1", 123);\nhashtable.Add("chave2", "string"); // Erro? Não, vai compilar!\nhashtable.Add(456, "mais uma string"); // A chave também pode ser qualquer objeto\n\n// Agora vamos tentar recuperar os dados\nint value = (int)hashtable["chave2"]; // Opa! InvalidCastException em tempo de execução!
\n\n

Viu o problema? Hashtable aceita qualquer tipo — tanto para chaves quanto para valores. O compilador deixará passar qualquer erro, e você só descobrirá durante a execução do programa. Isso é um caminho direto para bugs difíceis de detectar.

\n\n

Agora vejamos o Dictionary:

\n\n
// Jeito novo e correto: Dictionary\nDictionary<string, int> dictionary = new Dictionary<string, int>();\ndictionary.Add("chave1", 123);\n// dictionary.Add("chave2", "string"); // Erro de compilação! Tipo incompatível\n// dictionary.Add(456, "mais uma string"); // Também erro — a chave deve ser string\n\n// Recuperando dados sem conversões\nint value = dictionary["chave1"]; // Limpo, seguro, elegante
\n\n

Dictionary é type-safe. Você especifica explicitamente quais tipos serão usados para chaves e valores. O compilador verifica isso em tempo de compilação. Sem exceções inesperadas em tempo de execução — o código ou funciona, ou não compila.

\n\n

Desempenho: quem é mais rápido?

\n\n

O segundo aspecto importante é o desempenho. E aqui o Dictionary novamente sai na frente. Por quê? Tudo se resume ao boxing e unboxing.

\n\n

Quando você trabalha com Hashtable, todos os tipos por valor (int, double, struct, etc.) são empacotados em objetos. Isso cria carga para o garbage collector e torna a execução mais lenta. Vamos verificar na prática:

\n\n
// Hashtable: boxing/unboxing\nHashtable ht = new Hashtable();\nStopwatch sw = Stopwatch.StartNew();\nfor (int i = 0; i < 1000000; i++)\n{\n    ht.Add(i, i); // Cada int é empacotado em object\n}\nsw.Stop();\nConsole.WriteLine($"Hashtable inserção: {sw.ElapsedMilliseconds} ms");\n\n// Dictionary: sem boxing\nDictionary<int, int> dict = new Dictionary<int, int>();\nsw.Restart();\nfor (int i = 0; i < 1000000; i++)\n{\n    dict.Add(i, i); // Sem boxing — trabalhamos com int diretamente\n}\nsw.Stop();\nConsole.WriteLine($"Dictionary inserção: {sw.                

Blogs

Book Recommendations