174

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

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

有一个更好的方法吗?有没有办法就地过滤,所以不需要临时列表?

4

9 回答 9

281

如果您使用的是 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;在文件顶部导入。

于 2008-08-25T15:13:51.927 回答
21

这是使用三种不同方法进行列表过滤的代码块/示例,我将它们放在一起展示基于 Lambda 和 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
于 2008-08-25T15:38:11.993 回答
17

List<T>有一种FindAll方法可以为您进行过滤并返回列表的子集。

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

编辑:我在对 LINQ 和Where()方法有很好的理解之前写了这篇文章。如果我今天写这篇文章,我可能会使用 Jorge 上面提到的方法。如果您被困在 .NET 2.0 环境中,该FindAll方法仍然有效。

于 2008-08-25T15:14:40.303 回答
8

您可以使用 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 函数,并且在某些情况下,您不使用过滤集合中的所有项目,它可能会提供一些良好的性能增益。

于 2008-08-25T15:16:03.107 回答
4

要做到这一点,您可以使用“List<>”类的 RemoveAll 方法以及自定义“Predicate”类......但所做的只是清理代码......在引擎盖下它正在做同样的事情你是什​​么......但是是的,它就地完成了,所以你做同样的临时列表。

于 2008-08-25T15:15:41.267 回答
4

您可以使用 List 的FindAll方法,提供要过滤的委托。不过,我同意@IainMH的观点,除非它是一个巨大的列表,否则不值得让自己担心太多。

于 2008-08-25T15:16:23.090 回答
3

如果您使用的是 C# 3.0,则可以使用 linq

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

var filteredList = from x in myList
                   where x > 7
                   select x;
于 2008-08-25T15:23:04.540 回答
3

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

于 2010-10-12T15:04:15.380 回答
1

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

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

标准过滤每次都需要 O(n)。

于 2016-06-30T11:21:53.267 回答