2008-08-25 3 views
92

Я ищу очень быстрый способ фильтрации коллекции на C#. В настоящее время я использую общий список < объектов >, но я открыт для использования других структур, если они работают лучше.Фильтрация коллекций в C#

В настоящее время я просто создаю новый список < объект > и зацикливаю через первоначальный список. Если критерии фильтрации совпадают, я помещаю копию в новый список.

Есть ли лучший способ сделать это? Есть ли способ фильтрации на месте, чтобы не требовалось временного списка?

+0

Это будет невероятно быстро. Это заставляет вашу систему замедляться? Is * огромный * список? В противном случае я не стал бы волноваться. – 2008-08-25 15:13:00

ответ

151

Если вы используете C# 3.0 вы можете использовать LINQ, способ лучше и способ более элегантный:

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

Метод расширения Where IEnumerable , а не список . Это должно быть: myList.Where (x => x> 7) .ToList() – 2010-01-21 05:29:32

+0

Спасибо Rafa за ваш комментарий. – David 2010-02-19 15:45:00

9

Список имеет метод FindAll, который будет делать фильтрацию для вас и возвращает подмножество списка.

MSDN от имеет большой пример кода здесь: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

EDIT: Я написал это раньше у меня было хорошее понимание Linq и метод Where(). Если бы я написал это сегодня, я бы, вероятно, использовал метод, о котором упоминал Хорхе. Метод FindAll по-прежнему работает, если вы застряли в среде .NET 2.0.

3

Чтобы сделать это на месте, вы можете использовать метод RemoveAll класса «List <>» вместе с обычным классом «Предикат» ... но все, что делает, это очистить код ... под капотом он делает то же самое, что и вы ... но да, он делает это на месте, поэтому вы делаете тот же список тем.

6

Вы можете использовать IEnumerable для устранения необходимости временного списка.

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

где Матчи - это имя вашего фильтра. И вы можете использовать это как:

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

Это будет вызывать GetFilteredItems функции при необходимости, и в некоторых случаях, которые вы не используете все элементы в отфильтрованной коллекции, он может обеспечить хороший прирост производительности.

2

Метод FindAll списка, предоставляющий делегату для фильтрации. Хотя, я согласен с @IainMH, что не стоит слишком беспокоиться о себе, если это не огромный список.

1

Если вы используете C# 3.0 вы можете использовать LINQ

Или, если вы предпочитаете использовать специальный синтаксис запроса предоставленный C# 3 компилятор:

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

Вот блок кода/пример некоторой фильтрации списков, используя три разных метода, которые я собрал, чтобы показать фильтрацию списка Lambdas и 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

Использование Linq относительно медленнее, чем использование предиката, предоставляемого методу List FindAll. Также нужно быть осторожным с Linq, поскольку подсчет списка фактически не выполняется до тех пор, пока вы не получите доступ к результату.Это может означать, что, когда вы думаете, что создали отфильтрованный список, контент может отличаться от того, что вы ожидали, когда вы его действительно прочитали.

0

Если ваш список очень большой, и вы многократно фильтруете - вы можете отсортировать исходный список атрибута фильтра, бинарный поиск, чтобы найти начальную и конечную точки.

Начальное время O (n * log (n)), то O (log (n)).

Стандартная фильтрация будет принимать O (n) каждый раз.

Смежные вопросы