Pergunta

Estou procurando uma maneira muito rápida de filtrar uma coleção em C#.Atualmente, estou usando coleções genéricas de List<object>, mas estou aberto a usar outras estruturas se elas tiverem melhor desempenho.

Atualmente, estou apenas criando um novo List<object> e percorrendo a lista original.Se os critérios de filtragem corresponderem, coloco uma cópia na nova lista.

Existe uma maneira melhor de fazer isso?Existe uma maneira de filtrar para que não seja necessária uma lista temporária?

Foi útil?

Solução

Se você estiver usando C # 3.0, poderá usar o linq, muito melhor e mais elegante:

List<int> myList = GetListOfIntsFromSomewhere();

// This will filter out the list of ints that are > than 7, Where returns an
// IEnumerable<T> so a call to ToList is required to convert back to a List<T>.
List<int> filteredList = myList.Where( x => x > 7).ToList();

Se você não consegue encontrar o .Where, isso significa que você precisa importar using System.Linq; no topo do seu arquivo.

Outras dicas

Aqui está um bloco de código/exemplo de alguma filtragem de lista usando três métodos diferentes que juntei para mostrar a filtragem de lista baseada em Lambdas e LINQ.

#region List Filtering

static void Main(string[] args)
{
    ListFiltering();
    Console.ReadLine();
}

private static void ListFiltering()
{
    var PersonList = new List<Person>();

    PersonList.Add(new Person() { Age = 23, Name = "Jon", Gender = "M" }); //Non-Constructor Object Property Initialization
    PersonList.Add(new Person() { Age = 24, Name = "Jack", Gender = "M" });
    PersonList.Add(new Person() { Age = 29, Name = "Billy", Gender = "M" });

    PersonList.Add(new Person() { Age = 33, Name = "Bob", Gender = "M" });
    PersonList.Add(new Person() { Age = 45, Name = "Frank", Gender = "M" });

    PersonList.Add(new Person() { Age = 24, Name = "Anna", Gender = "F" });
    PersonList.Add(new Person() { Age = 29, Name = "Sue", Gender = "F" });
    PersonList.Add(new Person() { Age = 35, Name = "Sally", Gender = "F" });
    PersonList.Add(new Person() { Age = 36, Name = "Jane", Gender = "F" });
    PersonList.Add(new Person() { Age = 42, Name = "Jill", Gender = "F" });

    //Logic: Show me all males that are less than 30 years old.

    Console.WriteLine("");
    //Iterative Method
    Console.WriteLine("List Filter Normal Way:");
    foreach (var p in PersonList)
        if (p.Gender == "M" && p.Age < 30)
            Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //Lambda Filter Method
    Console.WriteLine("List Filter Lambda Way");
    foreach (var p in PersonList.Where(p => (p.Gender == "M" && p.Age < 30))) //.Where is an extension method
        Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //LINQ Query Method
    Console.WriteLine("List Filter LINQ Way:");
    foreach (var v in from p in PersonList
                      where p.Gender == "M" && p.Age < 30
                      select new { p.Name, p.Age })
        Console.WriteLine(v.Name + " is " + v.Age);
}

private class Person
{
    public Person() { }
    public int Age { get; set; }
    public string Name { get; set; }
    public string Gender { get; set; }
}

#endregion

List possui o método FindAll que fará a filtragem para você e retornará um subconjunto da lista.

O msdn tem um ótimo exemplo de código aqui: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

EDITAR:Escrevi isso antes de ter um bom entendimento do Linq e do método Where().Se eu escrevesse isso hoje provavelmente usaria o método que Jorge menciona acima.O método FindAll ainda funciona se você estiver preso em um ambiente .NET 2.0.

Você pode usar IEnumerable para eliminar a necessidade de uma lista temporária.

public IEnumerable<T> GetFilteredItems(IEnumerable<T> collection)
{
    foreach (T item in collection)
    if (Matches<T>(item))
    {
        yield return item;
    }
}

onde Matches é o nome do seu método de filtro.E você pode usar isso como:

IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
    // do sth with your filtered items
}

Isso chamará a função GetFilteredItems quando necessário e, em alguns casos em que você não usa todos os itens da coleção filtrada, pode fornecer um bom ganho de desempenho.

Você pode usar o Encontrar tudo método da Lista, fornecendo um delegado para filtrar.Porém, eu concordo com @IainMH que não vale a pena se preocupar muito, a menos que seja uma lista enorme.

Para fazer isso no local, você pode usar o método RemoveAll da classe "List<>" junto com uma classe "Predicate" personalizada... mas tudo o que faz é limpar o código...nos bastidores, ele está fazendo a mesma coisa que você ... mas sim, ele faz isso no lugar, então você faz a mesma lista temporária.

Usar Linq é relativamente mais lento do que usar um predicado fornecido ao método Lists FindAll.Também é preciso ter cuidado com o Linq, pois a enumeração da lista não é realmente executada até que você acesse o resultado.Isso pode significar que, quando você pensa que criou uma lista filtrada, o conteúdo pode ser diferente do que você esperava quando realmente a leu.

Se você estiver usando C# 3.0, você pode usar linq

Ou, se preferir, use a sintaxe de consulta especial fornecida pelo compilador C# 3:

var filteredList = from x in myList
                   where x > 7
                   select x;

Se sua lista for muito grande e você estiver filtrando repetidamente - você pode classificar a lista original no atributo de filtro, pesquisa binária para encontrar os pontos inicial e final.

Tempo inicial O(n*log(n)) depois O(log(n)).

A filtragem padrão levará O(n) cada vez.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top