Фильтрация коллекций в C#
-
09-06-2019 - |
Вопрос
Я ищу очень быстрый способ отфильтровать коллекцию на C#.В настоящее время я использую общие коллекции List<object>, но готов использовать и другие структуры, если они работают лучше.
В настоящее время я просто создаю новый List<object> и просматриваю исходный список.Если критерии фильтрации совпадают, я помещаю копию в новый список.
Есть лучший способ сделать это?Есть ли способ фильтровать данные, чтобы не требовался временный список?
Решение
Если вы используете 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;
в верхней части вашего файла.
Другие советы
Вот блок кода/пример некоторой фильтрации списков с использованием трех разных методов, которые я собрал вместе, чтобы показать фильтрацию списков на основе 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
В списке есть метод FindAll, который выполнит фильтрацию и вернет подмножество списка.
В msdn есть отличный пример кода: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx
РЕДАКТИРОВАТЬ:Я написал это до того, как хорошо разобрался в 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;
}
}
где Matches — это имя вашего метода фильтрации.И вы можете использовать это так:
IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
// do sth with your filtered items
}
Это вызовет функцию GetFilteredItems, когда это необходимо, и в некоторых случаях, когда вы не используете все элементы в отфильтрованной коллекции, это может обеспечить хороший прирост производительности.
Чтобы сделать это на месте, вы можете использовать метод RemoveAll класса «List<>» вместе с пользовательским классом «Predicate»… но все, что для этого нужно, — это очистить код…под капотом он делает то же самое, что и вы... но да, он делает это на месте, поэтому вы делаете то же самое со временным списком.
Использование Linq относительно медленнее, чем использование предиката, предоставленного методу Lists FindAll.Также нужно быть осторожным с Linq, поскольку перечисление списка фактически не выполняется, пока вы не получите доступ к результату.Это может означать, что когда вы думаете, что создали отфильтрованный список, его содержимое может отличаться от того, что вы ожидали, когда действительно его читали.
Если вы используете С# 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).