Post: [Generics] Listas genéricas – TDictionary

Alessandro Medeiros

Fala ai Radizeiros e Radizeiras, tudo bem com vocês?

Chegamos ao último post sobre as listas genéricas, mas não fique triste, iremos ainda ter mais posts sobre generics.

Mas esse é o último das quatro listas genéricas que possuímos dentro do Delphi.

Já vimos em posts anteriores as listas TList, TQueue, TStack, e agora iremos ver o TDictionary.

Ele é muito legal e utilizo muito em meus projetos.

Como já de costume, irei mostrar um pequeno exemplo já pronto com todos os métodos que podemos utilizar dentro do TDictionary.

Como eu disse antes, utilizo demais o TDictionary, porque ele tem uns recursos muitos legais, que nos ajudam muito nas implementações de alguns padrões de projetos, como por exemplo o padrão Proxy.

Vocês irão ver o porque o TDictionary é muito utilizado, ele possui recursos legais e fantásticos, são uma mão na roda na hora de criarmos listas e principalmente as listas genéricas.

Chega de muito papo e vamos para dentro do Delphi.

Já estou com meu exemplo aqui pronto, do TDictionary, e ele tem bastante funções, mais funções do que as outras listas.

Primeira coisa que devemos fazer para a utilização da lista, é instanciá-la, e para isso irei criar uma variável para a lista genérica do private do formulário deste exemplo.

private
    Lista : TDictionary<String, TPessoa>;

Observe que a variável criada do tipo genérico TDictionary recebe dois parâmetros, e esses parâmetros são genéricos, sendo assim podendo receber qualquer tipo, sendo eles, uma chave, e um valor.

Então dentro dessa lista você tem uma chave e um valor, que através dessa chave, chave essa indexada, você consegue encontrar o valor que você quer dentro da lista fazendo referência a essa chave.

Isso se torna muito útil em nosso dia a dia.

Então como eu adiciono um valor dentro dessa lista?

Primeira coisa que iremos fazer dentro do TDictionary é adicionar um valor.

Mas primeiro para trabalhar com o TDictionary, e o exemplo ficar mais legal, criei uma classe TPessoa, que possui Nome e CPF.

TPessoa : record
    CPF : String;
    Nome : String;
end;

Dentro da nossa lista a chave irá ser o CPF, e dentro do valor será adicionado o objeto TPessoa, com todas as informações, nesse exemplo só terá duas, CPF e Nome, mas você poderá ter uma classe com diversas informações.

Observando o nosso exemplo eu possuo dois campos, mais preciso dois edits, que seria CPF e Nome.

E no botão com o nome Add iremos adicionar os valores dos campos a nossa lista, observe o código abaixo.

procedure TForm1.Button1Click(Sender: TObject);
begin
  FPessoa.CPF := edtCPF.Text;
  FPessoa.Nome := edtNome.Text;
  Lista.Add(edtCPF.Text,Pessoa);
end;

Na hora de adicionar os valores dos edits, eu primeiro criei uma variável local chamada Pessoa do tipo TPessoa, logo em seguida eu preencho as propriedades CPF e Nome, com os valores que estão no edit, e executo o método de adicionar do TDictionary.

Na hora de adicionar, se você observar na imagem abaixo, ele pede uma chave do tipo string, e um valor do tipo TPessoa.

Esse método Add do TDictionary está solicitando esses tipos, porque definimos na declaração do TDictionary, e não podemos esquecer de que sempre que estiver trabalhando com uma lista, devemos primeiro instanciar esse objeto, e claro, não podemos esquecer da classe TPessoa.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Lista := TDictionary<String,TPessoa>.Create;
end;

No create do formulário eu chamei a variável Lista e instanciei o TDictionary que vai ter uma chave do tipo string, e um valor do tipo pessoa, e mando criar.

Lembrando esses dois valores são genéricos, logo eles poderia ser qualquer coisa, ou seja, qualquer tipo, sejam eles primitivo ou classe.

Vamos executar o exemplo e adicionar um objetos na lista.

Adicionamos na lista o Thulio com CPF 111.

Para que possamos buscar registros dentro da lista utilizamos um carinha muito legal chamado TryGetValue.

O TryGetValue é como se fosse um try except.

procedure TForm1.Button2Click(Sender: TObject);
var
Pessoa : TPessoa;
begin
  if Lista.TryGetValue(edtCPF.Text, Pessoa) then
    Memo1.Lines.Add(Pessoa.Nome + ' - ' + Pessoa.CPF)
  else
    Memo1.Lines.Add('Usuário não encontrado');
end;

Se reparar nos requisitos da execução do TryGetValue, ele aguarda uma chave e um valor, mas se reparar na imagem abaixo o valor está como Out, então será a saída.

Todas as vezes que chamar o TryGetValue, como no código acima, dentro de uma estrutura condicional e passo um CPF, que é a chave, e logo um objeto do tipo da classe TPessoa.

O TryGetValue, percorre todas a lista, e verifica se existe a chave que foi passada dentro da classe TPessoa.

Se ele encontrar alguém que tem essa chave na classe TPessoa, é retornado o objeto que foi passado preenchido.

A partir disso eu utilizo o objeto Pessoa, pegando os seus valores.

Dentro do minha estrutura condicional, no caso de verdadeiro, se a chave existir dentro da lista, ele irá retornar o objeto com o valor encontra, e sendo assim podendo ser trabalhado esse objeto.

Graças a chave eu posso ir diretamente no objeto sem precisar ter que utilizar uma estrutura de repetição para saber em que ponto está o meu valor.

Como é feito no TList, onde temos que varrer toda a lista para encontrar o objeto, no caso do TDictionary podemos ir direto ao objeto, na sua posição, funciona como um index para nós.

Agora que já adicionamos o Thulio com o CPF 111, vamos adicionar o José com o CPF 222.

Observe que logo após ter adicionado o José na lista, verificamos com o TryGetValue se existe alguém com o CPF 333, recebo uma resposta no Memo de que não foi encontrado, logo em seguida se existe com o CPF 111, foi retornado o Thulio, com o CPF 222, retornado o José, e por fim com o CPF 444, usuário não encontrado.

Viu como é muito fácil de fazer uma busca, o TryGetValue serve exatamente para fazer uma busca dentro da lista

Muito legal né?

Por este motivo que utilizo muito o TDictionary no meu dia a dia.

Agora temos a opção de remover no TDictionary.

A implementação do remover é muito simples.

procedure TForm1.Button3Click(Sender: TObject);
begin
  Lista.Remove(edtCPF.Text);
end;

Como já possuímos a chave, na hora de remover ele solicita essa chave, e como essa chave é o CPF eu passo para esse método o edit pertinente a ele.

 

Observe que foi passado para o Edit o CPF 222, e logo em seguida eu pesquisei se existe ainda o 222 na lista, e foi retornado que não existe o usuário na lista.

Agora o José não existe mais na lista, pois acabei de remover ele pela chave.

Eu não irei falar sobre o TrimExcess que você já deve estar acostumado pois bati muito nele nos posts anteriores nas outras lista, onde ele comprime a capacidade da lista para o seu tamanho real liberando espaços na memória.

Mas temos um método também muito legal no TDictionary que é o ContaintKey, que verifica se contém uma chave, se realmente essa chave existe.

Observe que executei o método do ContainsKey, verificando se existe o CPF 222, e foi retornado que o usuário não foi encontrado, agora ao buscar pelo CPF 111, ele retornou que encontrou o usuário.

Vamos ver como é a implementação desse método.

procedure TForm1.Button5Click(Sender: TObject);
begin
  if Lista.ContainsKey(edtCPF.Text) then
    Memo1.Lines.Add('Usuário Encontrado')
  else
    Memo1.Lines.Add('Usuário não Encontrado');
end;

O método Containskey não retorna o objeto, como vimos no TryGetValue, ele só me retorna se a chave existe ou não.

Da mesma forma temos o método o Containsvalue, que é para verificar se o valor existe.

procedure TForm1.Button6Click(Sender: TObject);
var
Pessoa : TPessoa;
begin
  Pessoa.Nome := edtNome.Text;
  Pessoa.CPF := edtCPF.Text;
  
  if Lista.ContainsValue(Pessoa) then
    Memo1.Lines.Add('Usuario Encontrado')
  else
    Memo1.Lines.Add('Usuario não Encontrado');
end;

O value neste caso é a classe TPessoa, então no código acima foi passado para o objeto TPessoa os valores dos edits, e logo verifico se o valor existe ou não dentro da lista.

Viu que ao verificar se existe o cpf 111 ele me retorna que existe, mas o value é José e não o Thulio que foi retornado.

Então ao executar o método de ContainsValue verifica se existe o objeto com o CPF 111 e com o Nome Thulio, é retornado que existe.

Observando a chave existe, mas o objeto num todo não é igual, então você consegue validar o objeto que está dentro do TDictionary.

Outro método muito legal que temos no TDictionary é o AddOrSetValue.

procedure TForm1.Button7Click(Sender: TObject);
var
  Pessoa : TPessoa;
begin
  Pessoa.Nome := edtNome.Text;
  Pessoa.CPF := edtCPF.Text;
  Lista.AddOrSetValue(Pessoa.CPF, Pessoa);
end;

Esse método é como se fosse um adicionar na lista, mas ele verifica se existe o CPF ou a chave, se esta chave já existir, ao adicionar um novo ele irá substituir, ou seja, altera o valor já existente na lista.

O TDictionary possui dois métodos que nos proporciona varre a lista, a lista de keys e a lista de value, possibilitando varrer tanto a lista de chave quanto a de valores.

Dentro do Lista Key eu executo um for, como você pode observar no código abaixo.

procedure TForm1.Button8Click(Sender: TObject);
var
  key : string;
begin
  for Key in Lista.Keys do
    Memo1.Lines.Add(key);
end;

Viu como é simples varrer todas as chaves?

Viu que adicionei mais objetos na lista, e ao executar o lista key, retorna todos os CPF que foram adicionados na lista.

Mesma coisa funciona para nossa lista de valores.

procedure TForm1.Button9Click(Sender: TObject);
var
  Value : TPessoa;
begin
  for Value in Lista.Values do
    Memo1.Lines.Add(Value.Nome);
end;

Como o valor é do tipo TPessoa, foi necessário criar uma variável do tipo TPessoa e executar o for in, onde a variável recebe cada um dos valores dessa interação.

Como anteriormente só adicionamos os CPF diferentes, o valor foi mantido no edit, sendo assim foi retornado todos o Thulio adicionados.

Temos também a opção de limpar a lista com o Clear.

procedure TForm1.Button10Click(Sender: TObject);
begin
  Lista.Clear;
end;

E temos dois camaradinhas que acho muito legais, que são os eventos.

Mas Thulio, o que são esses eventos?

No post anterior que falei sobre o TList, foi mostrado os eventos.

O que são esses evento?

São as possibilidade de monitorar, comportamentos do TDictionary, e posso monitorar os comportamento tanto da chave, quanto do valor.

Todas as vezes, que for removido, inserido ou extraído uma chave ou um valor de dentro do TDictionary, recebo essas informações.

E como fazemos para receber essa informação?

Dentro da lista eu tenho o OnKeyNotify e OnValueNotify.

[imagem das propriedades da lista mostrando esses dois eventos]

Observe que este dois eventos esperam receber dois métodos com essas assinaturas.

Então qualquer método que tiverem os parâmetros esperados por esses dois eventos, podem ser setados nessas propriedades.

procedure TForm1.KeyNotify(Sender: TObject; const item: String;
  Action: TCollectionNotification);
begin
  case Action of
    cnAdded: Memo1.Lines.Add('A Chave ' + item +' foi adicionada');
    cnExtracted: Memo1.Lines.Add('A Chave ' + item +' foi extraida');
    cnRemoved: Memo1.Lines.Add('A Chave ' + item +' foi removida');
  end;
end;

Observe que criei um método com a assinatura esperada pela a propriedade OnKeyNotify, e criei um case onde ele verifica qual foi a ação realizada para o key.

A mesma coisa para o OnValueNotify.

procedure TForm1.ValueNotify(Sender: TObject; const item: TPessoa;
  Action: TCollectionNotification);
begin
  case action of
    cnAdded: Memo1.Lines.Add('O Value ' + item.Nome +' foi adicionado');
    cnExtracted: Memo1.Lines.Add('O Value ' + item.Nome +' foi extraitdo');
    cnRemoved: Memo1.Lines.Add('O Value ' + item.Nome +' foi removido');
  end;
end;

Observe que a única diferença é que o OnKeyNotify espera uma constante do tipo string, que no caso a chave é uma string, e como o value é do tipo TPessoa, ele espera receber um item do tipo TPessoa.

Muito simples né pessoal?

Trabalhar com o TDictionary facilita de mais o nosso dia a dia, e espero que facilite o seu também.

É chegamos a final dessa parte da série onde falamos das listas genéricas, mas teremos ainda mais posts falando sobre o generics.

Tem mais conteúdos para explorarmos nos generics.

Antes de irmos para uma nova série, e claro, os conteúdos não podem parar, toda semana conteúdos novos aqui no blog.

Espero que você esteja curtindo muito as nossas séries, e se você não conhecia o TDictionary, essa é a hora, não perca tempo e começa a usar, porque você terá grande produtividade.

Várias coisas que você está fazendo manualmente, pode ser substituído por poucas linhas de códigos usando o TDictionary.

E caso você tenha interesse de conhecer mais sobre generics acessa o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI, onde você não só terá conteúdos relacionados aos generics, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.

CLIQUE AQUI E SAIBA MAIS SOBRE O CLUBE DOS PROGRAMADORES DELPHI




Faça sua busca

CATEGORIAS

POSTS RECENTES

E caso você tem interesse de conhecer mais sobre [Generics] Listas genéricas – TDictionary, acesse o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI
Você não terá só conteúdos relacionados ao [Generics] Listas genéricas – TDictionary, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.
Gostou?
Compartilhe:

Embarque no foguete com milhares de devs para aprender desenvolvimento, evoluir de forma contínua e se manter relevante no mercado.

Dúvidas
Cadastre-se em nossa lista

Para ter acesso em primeira mão, a tudo que acontece na Academia do Código, basta se cadastrar em nossa lista

Grupo Thulio Bittencourt | Academia do Código

#FaçaPartedaHistória

Copyright © 2024 – Todos os direitos reservados