在 C # 中过滤集合

我正在寻找一种非常快速的方法来过滤 C # 中的一个集合。我目前正在使用通用 List<object>集合,但是如果它们的性能更好,我也可以使用其他结构。

目前,我只是创建一个新的 List<object>和循环通过原始列表。如果筛选条件匹配,则将一个副本放入新列表中。

还有更好的办法吗?是否有办法适当地进行过滤,以便不需要临时列表?

346107 次浏览

如果你使用的是 C # 3.0,你可以使用 linq,它更好,也更优雅:

List<int> myList = GetListOfIntsFromSomewhere();


// This will filter ints that are not > 7 out of the list; Where returns an
// IEnumerable<T>, so call ToList to convert back to a List<T>.
List<int> filteredList = myList.Where(x => x > 7).ToList();

如果您找不到 .Where,那意味着您需要在文件的顶部导入 using System.Linq;

List<T>有一个 FindAll方法,它将为您执行过滤并返回列表的一个子集。

MSDN 有一个很好的代码示例: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

编辑: 在我对 LINQ 和 Where()方法有一个很好的理解之前,我写了这篇文章。如果我今天写这篇文章,我可能会使用 Jorge 上面提到的方法。如果陷入。NET 2.0环境。

你可以使用“ List < >”类的 RemoveAll 方法和一个自定义的“谓词”类... 但是所有这些只是清理代码... 在底层它正在做和你一样的事情... 但是是的,它在适当的地方做它,所以你做相同的临时列表。

您可以使用 IEnumable 来消除对临时列表的需要。

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 函数,在某些情况下,如果您没有使用筛选集合中的所有项,那么它可能会提供一些很好的性能增益。

您可以使用 List 的 FindAll方法,提供一个要筛选的委托。不过,我同意@伊恩的观点,即除非是一个巨大的清单,否则不值得过分担心。

如果使用 C # 3.0,可以使用 linq

或者,如果您愿意,可以使用 C # 3编译器提供的特殊查询语法:

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

下面是使用三种不同方法进行列表过滤的代码块/示例,我将这些方法放在一起,以显示基于 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

使用 LINQ 比使用 Lists FindAll方法提供的谓词要慢得多。还要小心使用 LINQ,因为在访问结果之前,list的枚举不会实际执行。这可能意味着,当您认为已经创建了一个过滤列表时,其内容可能与您实际阅读时所期望的内容不同。

如果您的列表非常大,并且您正在重复过滤-您可以对 filter 属性上的原始列表进行排序,二进制搜索可以找到起点和终点。

初始时间 O (n * log (n))然后 O (log (n))。

标准过滤每次采用 O (n)。