Dictionary(Of) - Informação organizada
Introdução
"Grande responsabilidade advém de um grande poder" é uma frase que se pode aplicar práticamente a tudo o que conhecemos, e na programação informática não é diferente. A evolução tecnológica forçou os programadores informáticos a prevêr volumes de informação cada vez maiores e a velocidades cada vez maiores. É certo que o hardware também evoluíu ao ponto de facilitar a tarefa, mas nunca rápido o suficiente para não apertar muitas empresas ou programadores com conceitos que têm de funcionar, e rápido. Os programadores recorrem a várias técnicas para tratar os enormes volumes de dados. A mais conhecida é provavelmente o "caching"1.
O problema
O verdadeiro problema é como construír a nível abstracto um cache que seja realmente mais rápido e menos custoso do que ir à fonte da informação. E mais importante de tudo, que seja extremamente fácil de se referenciar. Para o efeito, podemos utilizar muitos tipos, mas vamos focar-nos no que deve ser um dos mais importantes para manter caches: Dictionary(Of) (ou Dicionário De). A utilidade dos dicionários pode ser adaptada para um sem fim de aplicações, para além dos caches.
Porquê dicionários?
- Porque são rápidos
São, de longe, mais rápidos que uma "Hash Table"2. De facto, conseguem ser 50% mais rápidos que "Hash Tables" em certas operações. - Porque são organizados
Tem acesso instantâneo a determinada informação identificada por uma chave única. - Porque não permitem chaves duplicadas
A informação está organizada por chaves e as chaves não se podem repetir o que é uma situação ideal. - Porque permitem múltiplos tipos de dados
Tanto as chaves como os seus valores podem ser, virtualmente, qualquer tipo de dados. - Porque têm os seus métodos de pesquisa
Para as necessidades básicas de uma pesquisa, os dicionários possuem métodos para se evitarem implementações de ciclos. - Porque os posso transportar para qualquer lado
Como os próprios dicionários são um tipo de dados, podem ser referenciados para, e em qualquer lado.
Como funcionam?
Passemos então à prática. É na verdade muito simples! Cada bloco de código abaixo representa uma operação distinta que se pode executar com um dicionário.
Criação de uma instância
É na criação da instância onde determinamos quais os tipos de dados a tratar com o dicionário.
Dim Pessoal As New Dictionary(Of Integer, String)
O primeiro tipo de dados, Integer
, representa a chave e o segundo, String
, o valor associado a essa chave.
Adicionar entradas no dicionário
Para adicionar entradas recorremos ao método Add
, cujos argumentos são chave e valor.
Dim Pessoal As New Dictionary(Of Integer, String)
Pessoal.Add(1, "Maria Albertina")
Pessoal.Add(2, "Amélia Gertrudes")
Referenciar uma entrada por chave
Para referenciar um valor, basta escrever o nome do dicionário e introduzir a chave entre parênteses.
Dim PessoaComID1 As String = Pessoal(1)
Referenciar uma entrada por chave em segurança
Para tentar apanhar um valor através de uma chave, que poderá existir ou não, utilizamos o método TryGetValue
, pois este método referencia o valor encontrado para uma variável, se encontrar o valor, e devolve um Boolean
a determinar se o encontrou ou não sem ser necessário controlar a excepção que daí poderia ser lançada.
Dim ValorEncontrado As String = ""
Dim Sucesso As Boolean
Sucesso = Pessoal.TryGetValue(122, ValorEncontrado)
Alterar um valor por chave
Para alterar um valor basta referenciarmos a entrada do dicionário através da chave e atribuír um valor.
Pessoal(1) = "Maria Josefina"
Remover uma entrada por chave
Para remover um valor, basta utilizar o método Remove
cujo argumento é a chave.
Pessoal.Remove(1)
Limpar todas as entradas
Para limpar todas as entradas baste recorrer ao método Clear
.
Pessoal.Clear()
Determinar existência de chave
Para determinar a existência de determinada chave sem recorrer à implementação de ciclos, usamos o método ContainsKey
.
Dim ExisteNoDicionario As Boolean
ExisteNoDicionario = Pessoal.ContainsKey(1)
Determinar existência de valor
Para determinar a existência de determinado valor sem recorrer À implementação de ciclos, usamos o método ContainsValue
.
Dim ExisteNoDicionario As Boolean
ExisteNoDicionario = Pessoal.ContainsValue("Amélia Gertrudes")
Para a operação básica de um dicionário, os blocos anteriores representam tudo o que precisa de saber. No entanto, existem outras operações para necessidades mais específicas. Vamos expôr algumas:
Percorrer todas as chaves de um dicionário
For Each Chave As Integer In Pessoal.Keys
Debug.Write("Chave do dicionário Pessoal: " & Chave.ToString)
Next
Percorrer todos os valores de um dicionário
For Each Valor As String In Pessoal.Values
Debug.Write("Valor do dicionário Pessoal: " & Valor)
Next
Percorrer todos os pares do dicionário
For Each Par As KeyValuePair(Of Integer, String) In Pessoal
Dim Chave As Integer = Par.Key
Dim Valor As String = Par.Value
Next
Utilizar tipos de dados compostos
Public Class Form1
'Criamos um tipo de dados composto, como uma estructura com alguns elementos
Private Structure Pessoa
Dim Nome As String
Dim Idade As Integer
Dim Morada As String
End Structure
'Declaramos o nosso dicionário com chaves Integer e valores do tipo composto Pessoa
Private Pessoal As New Dictionary(Of Integer, Pessoa)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Instanciamos um novo tipo composto Pessoa
Dim P As New Pessoa With {.Nome = "Clara", .Idade = 20, .Morada = "Rua dos morcões, Nº21"}
'Adicionamos ao dicionário
Pessoal.Add(1, P)
'E por fim acedemos ao que acabámos de inserir
Dim Nome As String = Pessoal(1).Nome
Dim Idade As Integer = Pessoal(1).Idade
Dim Morada As String = Pessoal(1).Morada
End Sub
End Class
Emparelhamento de dicionários
Um exemplo mais complexo será o emparelhamento de dicionários. Como as chaves e os valores podem ser virtualmente qualquer tipo de dados, podem inclusivé ser do seu próprio tipo de dados. No exemplo, emparelhamos 3 dicionários para obter uma estructura organizada.
Public Class Form1
'Instanciamos 3 dicionários para conter todos os dados, assim estructurados em 3 níveis.
'Podem existir "n" pessoas, cada uma tem "n" armazéns que contêm coisas. Cada coisa tem um preço
Private Pessoas As New Dictionary(Of String, Dictionary(Of String, Dictionary(Of String, Double)))
Private ArmazensPessoais As New Dictionary(Of String, Dictionary(Of String, Double))
Private ArmazensPessoais2 As New Dictionary(Of String, Dictionary(Of String, Double))
Private Coisas As New Dictionary(Of String, Double)
Private Coisas2 As New Dictionary(Of String, Double)
Private Coisas3 As New Dictionary(Of String, Double)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Adicionamos, a título experimental, alguns dados aqui
'Começamos por adiconar algumas coisas e o seu preço
Coisas.Add("telemovel", 99.99)
Coisas.Add("brincos", 89.98)
Coisas.Add("chaleira", 24.99)
Coisas2.Add("t-shirt", 9.99)
Coisas2.Add("computador", 499.95)
Coisas2.Add("secador de cabelo", 15)
Coisas3.Add("carro", 12000)
Coisas3.Add("telemovel", 199.99)
Coisas3.Add("televisao", 499.97)
'Depois adicionamos os armazéns, referenciando as coisas que neles existem
ArmazensPessoais.Add("Armazém da esquina", Coisas)
ArmazensPessoais.Add("Armazém das coisas roubadas", Coisas3)
ArmazensPessoais2.Add("Armazém ao lado do café", Coisas2)
'Por fim, duas pessoas cada uma com os seus armazéns
Pessoas.Add("Manuel Arnês", ArmazensPessoais)
Pessoas.Add("Maria Falcatrua", ArmazensPessoais2)
'Agora, para verificar que tudo se encontra perfeitamente armazenado e estructurado, executamos alguns
'testes
'Vamos verificar quantas coisas tem o Manuel no armazém da esquina
Dim CoisasDoManuel As Integer = Pessoas("Manuel Arnês")("Armazém da esquina").Count
'Vamos verificar quantos armazéns tem o Manuel
Dim ArmazensDoManuel As Integer = Pessoas("Manuel Arnês").Count
'Vamos verificar o preço do carro no armazém de coisas roubadas do Manuel
Dim PrecoCarroRoubado As Double = Pessoas("Manuel Arnês")("Armazém das coisas roubadas")("carro")
'E por fim, vamos verificar o preço do secador que está no único armazém da Maria
Dim PrecoSecador As Double = Pessoas("Maria Falcatrua")("Armazém ao lado do café")("secador de cabelo")
'Também podemos, através de alguma lógica de ciclos, obter informações como por exemplo
'todos os items de todos os armazéns do Manuel
For Each ParArmazem As KeyValuePair(Of String, Dictionary(Of String, Double)) In Pessoas("Manuel Arnês")
For Each ParCoisa As KeyValuePair(Of String, Double) In Pessoas("Manuel Arnês")(ParArmazem.Key)
MsgBox("Armazém: " & ParArmazem.Key & " Coisa: " & ParCoisa.Key & " Preço: " & ParCoisa.Value & "€")
Next
Next
End Sub
End Class
Ir mais além
A utilidade dos dicionários em caching é verdadeiramente incrível. Com alguma prática da sua mecânica e um pouco de imaginação consegue criar um cache abstracto capaz de melhorar a performance de uma aplicação até 1000% se necessário.
-
Mais informação sobre caches: http://pt.wikipedia.org/wiki/Cache ↩
-
Mais informação sobre Hash Tables: http://en.wikipedia.org/wiki/Hash_table ↩