Фільтрування колекцій у C #


142

Я шукаю дуже швидкий спосіб відфільтрувати колекцію в C #. Наразі я використовую загальні колекції List <object>, але я готовий використовувати інші структури, якщо вони працюють краще.

Наразі я просто створюю новий Список <об'єкт> і циклічно переглядаю оригінальний список. Якщо критерії фільтрації відповідають, я вкладаю копію в новий список.

Чи є кращий спосіб зробити це? Чи є спосіб фільтрації на місці, щоб тимчасовий список не потрібен?


Це буде палаючий швидкий. Це спричиняє сповільнення роботи вашої системи? Чи величезний список? Інакше я б не хвилювався.
Ієн Холдер

Відповіді:


237

Якщо ви використовуєте 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();

Якщо ви не можете знайти те .Where, це означає, що вам потрібно імпортувати using System.Linq;вгорі файлу.


19
Метод розширення де повертає IEnumerable <T>, а не список <T>. Воно повинно бути: myList.Where (х => х> 7) .ToList ()
Рафа Кастанеда

1
Як це працює для фільтрації за рядками. Як і пошук усіх елементів у списку рядків, що починаються з "ch"
joncodo

2
@JonathanO Ви можете використовувати методи всередині Func. listOfStrings.Where (s => s.StartsWith ("ch")). ToList ();
Майк Г

1
Чи є спосіб об’єктивувати запити linq? Наприклад, використовувати .Where(predefinedQuery)замість використання .Where(x => x > 7)?
XenoRo

2
@AlmightyR: Просто визначте це як метод, який бере один аргумент. Приклад: public bool predefinedQuery(int x) { return x > 7; }. Тоді ваше .Where(predefinedQuery)б добре працювало.
Дон

21

Ось блок коду / приклад фільтрації списків за допомогою трьох різних методів, які я зібрав, щоб показати фільтрацію списків на основі 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

14

List<T>має FindAllметод, який зробить фільтрацію для вас і поверне підмножину списку.

MSDN має чудовий приклад коду тут: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

EDIT: Я писав це ще до того, як добре зрозумів LINQ та Where()метод. Якби я сьогодні писав це, я, мабуть, застосував би метод, про який Хорхе згадує вище. Цей FindAllметод все ще працює, якщо ви застрягли в середовищі .NET 2.0.


4
Linq добре, але принаймні на одну величину повільніше, тому FindAll та методи фільтрації розширень (наприклад, у масиві є купа), які не покладаються на IEnumerable, все ще мають сенс для сценаріїв, коли продуктивність має значення. (FWIW, я отримав результати від фактора 7 до 50 більше часу, необхідного Linq та / або IEnumerable, як правило)
Філм

Чи є причина, що це не прийнята відповідь? Здається, це швидше, і синтаксис чіткіший (без toList ()) виклику в кінці.
Ран Лоттем

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, коли це потрібно, а в деяких випадках, якщо ви не використовуєте всі елементи у відфільтрованій колекції, це може забезпечити хороший приріст продуктивності.


4

Щоб зробити це на місці, ви можете скористатися методом RemoveAll класу "Список <>" разом із користувацьким класом "Присудок" ... але все, що потрібно зробити - це очищення коду ... під кришкою це робиться те саме що ти є… але так, це робить це на місці, тому ти робиш те саме тимчасовий список.


4

Ви можете використовувати метод FindAll у списку, надаючи делегата для фільтрації. Хоча я погоджуюся з @ IainMH, що не варто занадто турбуватися про себе, якщо це не величезний список.


3

Якщо ви використовуєте C # 3.0, ви можете використовувати linq

Або, якщо вам зручніше, використовуйте синтаксис спеціальних запитів, наданий компілятором C # 3:

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

3

Використання LINQ порівняно набагато повільніше, ніж використання предиката, наданого FindAllметоду Lists . Також будьте обережні з LINQ, оскільки перерахування listфактично не виконується, поки ви не отримаєте результат. Це може означати, що, якщо ви думаєте, що ви створили відфільтрований список, вміст може відрізнятися від того, що ви очікували, коли ви насправді його прочитали.


1

Якщо ваш список дуже великий і ви фільтруєте повторно - ви можете сортувати оригінальний список за атрибутом фільтра, двійковий пошук, щоб знайти початкову та кінцеву точки.

Початковий час O (n * log (n)), потім O (log (n)).

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.