Фильтрация коллекций в C#
Я ищу очень быстрый способ отфильтровать коллекцию в C#. В настоящее время я использую общие коллекции List, но открыт для использования других структур, если они работают лучше.
В настоящее время я просто создаю новый список и перебираю исходный список. Если критерии фильтрации совпадают, я помещаю копию в новый список.
есть ли лучший способ сделать это? Есть ли способ фильтровать на месте, чтобы не было временного списка требуется?
9 ответов:
Если вы используете 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();
вот блок кода / пример некоторой фильтрации списка с использованием трех различных методов, которые я собрал, чтобы показать фильтрацию списка на основе 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
List имеет метод FindAll, который будет выполнять фильтрацию для вас и возвращать подмножество списка.
в MSDN был пример кода здесь: http://msdn.microsoft.com/en-us/library/aa701359(против.80).аспн
EDIT: я написал это, прежде чем у меня было хорошее понимание Linq и метода Where (). Если бы я написал это сегодня, я бы, вероятно, использовал метод, упомянутый выше Хорхе. Метод FindAll по-прежнему работает, если вы застряли в среде .NET 2.0 хотя.
Вы можете использовать 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, когда это необходимо, и в некоторых случаях, когда вы не используете все элементы в отфильтрованной коллекции, это может обеспечить некоторое повышение производительности.
чтобы сделать это на месте, вы можете использовать метод RemoveAll класса "List" вместе с пользовательским классом "Predicate"...но все, что делает чистый код... под капотом он делает то же самое, что и вы...Но да, он делает это на месте, поэтому вы делаете то же самое в списке temp.
использование Linq относительно медленнее, чем использование предиката, поставляемого в метод Lists FindAll. Также нужно быть осторожным с Linq, поскольку eumeration списка фактически не выполняется, пока вы не получите доступ к результату. Это может означать, что когда вы думаете, что создали отфильтрованный список, содержимое может отличаться от того, что вы ожидали, когда на самом деле читали его.
Если вы используете C# 3.0, вы можете использовать linq
или, если вы предпочитаете, используйте специальный синтаксис запроса, предоставляемый компилятором C# 3:
var filteredList = from x in myList where x > 7 select x;
Если ваш список очень большой, и вы фильтруете повторно - вы можете отсортировать исходный список по атрибуту фильтра, двоичный поиск, чтобы найти начальную и конечную точки.
начальное время O(n*log(n)) затем O(log (n)).
стандартная фильтрация будет принимать O (n) каждый раз.
Comments