2008-08-25 37 views
92

Estoy buscando una forma muy rápida de filtrar una colección en C#. Actualmente estoy usando colecciones genéricas de la lista <objeto>, pero estoy abierto a usar otras estructuras si funcionan mejor.Filtrado de colecciones en C#

Actualmente, estoy creando un nuevo objeto List < > y haciendo un bucle a través de la lista original. Si el criterio de filtrado coincide, coloco una copia en la nueva lista.

¿Hay una mejor manera de hacerlo? ¿Hay alguna manera de filtrar en su lugar por lo que no se requiere una lista temporal?

+0

Eso va a ser increíblemente rápido. ¿Está causando que su sistema se desacelere? ¿Es una * gran * lista? De lo contrario, no me preocuparía. –

Respuesta

151

Si estás usando C# 3.0 puede utilizar LINQ, mejor y de forma más elegante:

Lista
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(); 
+16

El método de extensión Where devuelve IEnumerable , no List . Debería ser: myList.Where (x => x> 7) .ToList() –

+0

Gracias Rafa por tu comentario. – David

+1

¿Cómo funciona esto para filtrar por cadenas? Como encontrar todos los elementos en una lista de cadenas que comienzan con "ch" – joncodo

9

tiene FindAll método que hará el filtrado para usted y devolver un subconjunto de la lista.

El MSDN tiene un gran ejemplo de código aquí: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

EDIT: He escrito esto antes de que tuviera una buena comprensión de LINQ y el método Donde(). Si tuviera que escribir esto hoy probablemente usaría el método que Jorge menciona arriba. El método FindAll todavía funciona si estás atrapado en un entorno .NET 2.0.

+1

Linq está bien, pero al menos una magnitud más lenta, por lo que FindAll y los métodos de extensión de filtrado (array tiene un montón de ellos por ejemplo) que no dependen de IEnumerable todavía tienen sentido para escenarios donde el rendimiento importa. (FWIW, obtuve los resultados del factor 7 a 50 más tiempo que necesitan Linq y/o IEnumerable, generalmente) – Philm

3

Para hacerlo en su lugar, puede utilizar el método RemoveAll de la clase "List <>" junto con una clase personalizada "Predicate" ... pero todo lo que hace es limpiar el código ... bajo el capó está haciendo lo mismo que tú ... pero sí, lo hace en su lugar, por lo que debes hacer lo mismo con la lista temporal.

6

Puede usar IEnumerable para eliminar la necesidad de una lista temporal.

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

donde Coincide con el nombre de su método de filtro. Y puede utilizar esto como:

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

Para ello, será GetFilteredItems función cuando sea necesario y en algunos casos que no utiliza todos los elementos de la colección filtrada, puede proporcionar una buena ganancia de rendimiento.

2

Puede usar el método FindAll de la Lista, proporcionando un delegado para filtrar. Sin embargo, estoy de acuerdo con @IainMH que no vale la pena preocuparse demasiado a menos que sea una lista enorme.

1

Si estás usando C# 3.0 puede utilizar LINQ

O, si lo prefiere, utilice la sintaxis de consulta especial proporcionada por el C# 3 compilador:

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

Aquí hay un bloque de código/ejemplo de algunos filtros de listas usando tres métodos diferentes que reuní para mostrar el filtrado de listas basadas en Lambda y 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 
2

El uso de Linq es relativamente más lento que el uso de un predicado suministrado al método FindAll de las Listas. También debe tener cuidado con Linq ya que la enumeración de la lista no se ejecuta realmente hasta que accede al resultado.Esto puede significar que cuando crea que ha creado una lista filtrada, el contenido puede diferir de lo que esperaba cuando realmente lo leyó.

0

Si su lista es muy grande y está filtrando repetidamente, puede ordenar la lista original en el atributo de filtro, búsqueda binaria para encontrar los puntos de inicio y fin.

Hora inicial O (n * log (n)) luego O (log (n)).

El filtrado estándar tomará O (n) cada vez.