在C#中过滤集合


142

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

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

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


那将非常快。是否会导致系统变慢?是一个庞大的清单吗?否则,我不会担心。
伊恩·霍尔德

Answers:


237

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


19
Where扩展方法返回IEnumerable <T>,而不返回List <T>。应该是:myList.Where(x => x> 7).ToList()
拉法

1
如何通过字符串进行过滤。就像在以“ ch”开头的字符串列表中查找所有项一样
-joncodo

2
@JonathanO您可以在Func内部使用方法。listOfStrings.Where(s => s.StartsWith(“ ch”))。ToList();
Mike G

1
有没有一种方法可以使linq查询客观化?例如,使用.Where(predefinedQuery)而不是使用.Where(x => x > 7)
XenoRo '16

2
@AlmightyR:只需将其定义为采用一个参数的方法即可。例如:public bool predefinedQuery(int x) { return x > 7; }。这样您.Where(predefinedQuery)就可以正常工作。

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

14

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

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

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


4
Linq很好,但至少慢了一个数量级,因此对于性能很重要的场景,不依赖IEnumerable的FindAll和筛选扩展方法(例如数组中有很多方法)仍然有意义。(FWIW,我得到的结果通常是Linq和/或IEnumerable所需的时间多出7到50倍)
Philm,

这是不是被接受的答案的原因吗?它看起来更快,语法更清晰(最后没有toList())调用。
Ran Lottem '19

6

您可以使用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函数,在某些情况下,如果您不使用过滤后的集合中的所有项目,则可能会获得一些良好的性能提升。


4

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


4

您可以使用List 的FindAll方法,以提供委托进行过滤。不过,我同意@ IainMH的观点,除非这是一个庞大的清单,否则不必担心太多。


3

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

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

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

3

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


1

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.