Deletado em 24 de junho de 2010

Procurando elementos em uma lista genérica (Exists(), Find(), FindAll(), RemoveAll())

Temos listas genéricas no Framework do .Net desde a versão 2.0. A primeira vez que eu vi já adorei pelo fato de aumentar o desempenho devido a não necessidade de conversão de tipos. Outro ponto é a segurança, pelo mesmo motivo da tipagem, antigamente nos ArrayLists poderíamos sem querer colocar uma string dentro de uma lista de inteiros, e só descobrir isso em execução. Por fim, também temos um código enxuto.

Então, lista genérica não é novidade, encontramos vários exemplos na Internet de como criá-las. Também encontramos exemplos de como fazer filtros ou encontrar objetos dentro das listas, mas são um pouco mais complexos de se achar e quando encontrados não são muito explicativos. Por isso então, este post vai pro lixo.

Vamos criar uma Lista de Pessoas, básico para estudar esse tipo de assunto (Vou usar um ConsoleApplication):

public class Pessoa
{
    private string Nome { get; set; }
    private Int16 Idade { get; set; }
    private char Sexo { get; set; }

    public Pessoa(string nome, Int16 idade, char sexo)
    {
        Nome = nome;
        Idade = idade;
        Sexo = sexo;
    }
    
    public Pessoa()
    {
         Nome = string.Empty;
         Idade = 0;
         Sexo = char.MinValue;
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<Pessoa> lstPessoas = new List<Pessoa>();

        lstPessoas.Add(new Pessoa("Adriano Galesso", 23, 'M'));
        lstPessoas.Add(new Pessoa("Manuela Bognar", 21, 'F'));
        lstPessoas.Add(new Pessoa("Pedro Alberto Braga", 44, 'M'));
        lstPessoas.Add(new Pessoa("Rosalinda da Silva", 75, 'F'));

        Existe(lstPessoas);
        Procura(lstPessoas);
        Remove(lstPessoas);

        Console.ReadLine();
    }
}

Temos uma lista com 4 objetos Pessoa. Agora vamos começar a “brincar” com nossa lista.

Primeiramente vamos criar um método que verifica se existem alguns nomes em nossa Lista. Para isso vamos utilizar a função Exists da lista genérica.

private static void Existe(List<Pessoa> lstPessoas)
{
    string[] nomePesquisa = new string[] { "Adriano", "Margarida", "Pedro", "Marcos", "Graciele" };

    for (int i = 0; i < nomePesquisa.Length; i++)
    {
        Console.WriteLine(lstPessoas.Exists(
            delegate(Pessoa pessoa)
            {
                return pessoa.Nome.IndexOf(nomePesquisa[i]) >= 0;
            }) ? 
            nomePesquisa[i] + " Existe na Lista" : 
            nomePesquisa[i] + " Não Existe na Lista");
    }

    Console.WriteLine("\n");
}

Neste método temos uma array de strings com nomes para pesquisar na lista. Para cada string vamos mostrar se existe ou não o nome da lista (mesmo que parte do nome).

Passamos uma lista de pessoas para o método fazer a verificação. Para a verificação foi utilizada a função Exists. Esta função espera um System.Predicate tipado, ou seja, um delegate que contém a regra para a busca dentro da lista. Sendo mais especifico, devemos delegar uma função para fazer qualquer condição que gostaríamos.

Neste caso o delegate fará a verificação de cada objeto pessoa em nossa lista, vendo se parte do Nome contém as strings da nossa array de pesquisa. Se tiver escrevemos uma coisa e se não tiver escrevemos outra.

Basicamente o mais importante de toda a busca é o delegate. Em uma função delegate, deve-se retornar sempre um boleano. Neste caso usamos o delegate junto com o próprio método, mas podemos fazer separadamente com um método qualquer. Vamos ver isso mais para frente.

Agora vamos conhecer a função Find() e FindAll(). A diferença entre as duas é bem simples. O Find() busca na lista, crescente, um resultado válido, caso encontre, ele para de buscar e retorna um resultado. Em contraponto o FindAll() procura por todos valores da lista e retorna uma outra lista de resultados válidos.

Vamos estudar o método Procura.

private static void Procura(List<Pessoa> lstPessoas)
{
    //Função Find retornando uma pessoa pelo Nome
    Pessoa pessoa = lstPessoas.Find(PorNome);
    Console.WriteLine(
        pessoa != null ? 
            "Pessoa Encontrada (No Find): " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo : 
            "Nenhuma pessoa Encontrada");

    //Função FindAll retornando uma lista de pessoas pelo Sexo
    foreach (Pessoa pessoaLocal in lstPessoas.FindAll(PorSexo))
    {
        Console.WriteLine("Pessoa Encontrada (No FindAll): " + pessoaLocal.Nome + ", " + pessoaLocal.Idade + ", " + pessoaLocal.Sexo);
    }

    Console.WriteLine("\n");
}

Este método começa utilizando a função Find da lista genérica. Assim como o método anterior, passamos a lista carregada.

Criamos um objeto pessoa, pois já sabemos que o Find retorna um resultado único. Podemos notar que não é utilizado o delegate, entretanto o Find também espera um System.Predicate.

Isto porque, agora chamamos direto um método, o PorNome.

private static bool PorNome(Pessoa pessoa)
{
    if (pessoa.Nome == "Adriano Galesso")
    {
        return true;
    }

    return false;
}

O método PorNome retorna um boleano (como dito anteriormente). E recebe uma pessoa, mas como isso se não passamos nada na chamada do método?

Como o predicate é tipado, automaticamente se sabe que objeto você está tentando procurar. Então no parâmetro do método é passado o objeto da lista (funciona como um foreach).

No método PorNome simplesmente fazemos uma validação qualquer e retornamos ao método Procura. Que verificará se o objeto pessoa não esta nulo e escrever que foi encontrado na lista.

Continuando no método Procura. Agora vamos utilizar a função FindAll() da lista genérica. Como já sabemos teremos uma lista de resultados, pois ele irá procurar em todos objetos da lista. O predicate que utilizaremos é o método PorSexo.

private static bool PorSexo(Pessoa pessoa)
{
    if (pessoa.Sexo == 'F')
    {
        return true;
    }

    return false;
}

Nada de diferente neste método, simplesmente vamos procurar em nossa lista todas as mulheres.

Voltando novamente no método Procura, como temos uma lista de resultados vamos usar o foreach para escrever os resultados com sucesso de nossa lista.

Por fim, vamos ver o método Remove.

private static void Remove(List<Pessoa> lstPessoas)
{
    lstPessoas.Remove(new Pessoa("Adriano Galesso", 23, 'M'));

    lstPessoas.RemoveAll(Jovens);

    foreach (Pessoa pessoa in lstPessoas)
    {
        Console.WriteLine("Velhos: " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo);
    }

    Console.WriteLine("\n");
}

Como anteriormente, passamos nossa lista de pessoas como parâmetro. Inicialmente usamos a função Remove da lista genérica. Que não tem nada de novo, simplesmente passamos um objeto que gostaríamos de remover e pronto, nada de predicate.

Depois disso vamos Remover todos os Jovens da Lista com o RemoveAll. Assim como o FindAll teremos uma lista de resultados. A única diferença é que uma vez removido da lista, não poderemos mais encontrar o objeto removido. (Por exemplo, como a lista é por referencia de memória, se chamássemos o método Remove antes dos outros, nossa lista estaria com menos objetos).

Utilizaremos como predicate o método Jovens.

private static bool Jovens(Pessoa pessoa)
{
    if (pessoa.Idade <= 30)
    {
        return true;
    }

    return false;
}

Vamos remover da lista, todas as pessoas com menos de 30 anos. Voltando ao método Remove, escrevemos a lista com os novos resultados.

Por fim, o mais legal das “buscas” em listas genéricas são os predicates. Mas ainda fica uma dúvida no ar. E se eu quiser passar mais parâmetros para um método delegate?

Bom, tem como, mas não fique todo felizão como eu fiquei quando descobri. Primeiro saiba que isso só é possível a partir do Framework 3.5 (meu projeto era no 2.0 =/).

Primeiramente se estivermos usando o Framework 3.5, podemos utilizar os Lambda Expression. Lambda Expression é a evolução dos delegates com pequenas notações, ou seja, se for só para retornar um valor simples, como por exemplo:

Método:
Double MultiplicaPorCinco(int x)
{
    return x * 5;
}

Lambda Expression:
x => x * 5;

Vamos aplicar no método Remove:

private static void Remove(List<Pessoa> lstPessoas)
{
    //lstPessoas.Remove(new Pessoa("Adriano Galesso", 23, 'M'));

    lstPessoas.RemoveAll(pessoa => pessoa.Idade <= 30);

    foreach (Pessoa pessoa in lstPessoas)
    {
        Console.WriteLine("Velhos: " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo);
    }

    Console.WriteLine("\n");
}

Se olharmos acima, veremos que foi substituído, o método Jovem pelo Lambda Expression.

Legal, e os 2 parâmetros na função?

Bom, isso vai ficar para o próximo Post, quando o lixo estiver cheio de Func =P e mais Lambda Expression

Abraços... PS: Recorri a um script para ajustar os códigos, é bem mais fácil =)

0 comentários:

Postar um comentário

Jogue sua opinião na lixeira!

Topo