在C#中过滤集合

在C#中过滤集合

Filtering collections in C#

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

当前,我只是创建一个新的List 并循环遍历原始列表。 如果过滤条件匹配,我将副本放入新列表。

有一个更好的方法吗? 有没有一种方法可以进行过滤,所以不需要临时列表?


如果您使用的是C#3.0,则可以使用linq,效果更好,更优雅:

1
2
3
4
5
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;


这是使用三种不同方法进行列表过滤的代码块/示例,我将它们组合在一起以展示基于Lambda和LINQ的列表过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#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< T >具有FindAll方法,该方法将为您进行过滤并返回列表的子集。

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

编辑:在我对LINQ和Where()方法有一个很好的了解之前,我曾写过这篇文章。如果今天要写这篇文章,我可能会使用Jorge上面提到的方法。但是,如果您陷在.NET 2.0环境中,FindAll方法仍然可以使用。


您可以使用IEnumerable消除临时列表的需要。

1
2
3
4
5
6
7
8
public IEnumerable< T > GetFilteredItems(IEnumerable< T > collection)
{
    foreach (T item in collection)
    if (Matches< T >(item))
    {
        yield return item;
    }
}

其中Matches是您的过滤方法的名称。您可以像这样使用:

1
2
3
4
5
IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
    // do sth with your filtered items
}

这将在需要时调用GetFilteredItems函数,在某些情况下,如果您不使用过滤后的集合中的所有项目,则可能会获得一些良好的性能。


您可以使用List的FindAll方法,以提供委托进行筛选。但是,我同意@IainMH的观点,除非列表太多,否则不必担心太多。


与使用提供给Lists FindAll方法的谓词相比,使用LINQ相对要慢得多。还请注意LINQ,因为在访问结果之前,实际上不会执行list的枚举。这可能意味着,当您认为自己已创建过滤列表时,其内容可能与实际阅读时的预期有所不同。


为此,您可以使用" List <>"类的RemoveAll方法以及自定义的" Predicate"类...但是所有要做的工作是清理代码...在后台进行相同的操作是的,但是,是的,它就位,所以您也可以执行临时列表。


If you're using C# 3.0 you can use linq

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

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

如果您的列表很大,并且要反复过滤-您可以在filter属性上对原始列表进行排序,然后通过二进制搜索找到起点和终点。

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

每次标准过滤将花费O(n)。


推荐阅读