C#でのコレクションのフィルタリング


142

C#でコレクションをフィルターする非常に高速な方法を探しています。私は現在、一般的なList <object>コレクションを使用していますが、パフォーマンスが優れている場合は他の構造を使用できます。

現在、私は新しいList <object>を作成し、元のリストをループしています。フィルタリング基準が一致する場合、コピーを新しいリストに入れます。

これを行うより良い方法はありますか?適所でフィルタリングする方法はありますので、一時的なリストは必要ありませんか?


それはものすごく速くなるでしょう。それはあなたのシステムを遅くしていますか?されている膨大なリストは?そうでなければ、私は心配しません。
Iainホルダー

回答:


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拡張メソッドは、List <T>ではなくIEnumerable <T>を返します。myList.Where(x => x> 7).ToList()
Rafa Castaneda

1
これは文字列によるフィルタリングにどのように機能しますか。"ch"で始まる文字列のリストですべてのアイテムを検索するのと同じように
joncodo

2
@JonathanO Func内でメソッドを使用できます。listOfStrings.Where(s => s.StartsWith( "ch"))。ToList();
Mike G

1
linqクエリをオブジェクト化する方法はありますか?例えば、使用する.Where(predefinedQuery)使用するのではなく.Where(x => x > 7)
XenoRo 2016

2
@AlmightyR:1つの引数を取るメソッドとして定義するだけです。例:public bool predefinedQuery(int x) { return x > 7; }。次に、あなた.Where(predefinedQuery)はうまくいきます。
Don

21

これは、LambdaとLINQベースのリストフィルタリングを示すために3つの方法を組み合わせて使用​​したリストフィルタリングのコードブロック/例です。

#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/en-us/library/aa701359(VS.80).aspx

編集:私は、LINQとWhere()メソッドを十分に理解する前にこれを書きました。今日これを書いたとしたら、私はおそらくホルヘが上で述べた方法を使うでしょう。FindAllただし、.NET 2.0環境で立ち往生している場合でも、この方法は機能します。


4
Linqは問題ありませんが、少なくとも1桁遅いため、IEnumerableに依存しないFindAllおよびフィルタリング拡張メソッド(たとえば、配列には多数のメソッドが含まれます)は、パフォーマンスが重要なシナリオでも有効です。(FWIW、Linqおよび/またはIEnumerableが必要とする通常
7〜50倍の

これが受け入れられない答えではない理由はありますか?それはより高速であるように見え、構文は最後に明確になります(toList()なし)。
Ran Lottem

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」クラスと一緒に使用できますが、実行することはコードをクリーンアップするだけです...内部では同じことをしていますあなたがしていること...しかし、はい、それは所定の場所でそれを行うので、あなたは同じ一時リストを行います。



3

C#3.0を使用している場合は、linqを使用できます

または、C#3コンパイラが提供する特別なクエリ構文を使用することもできます。

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

3

LINQを使用すると、Lists FindAllメソッドに提供された述語を使用するよりもかなり遅くなります。list結果にアクセスするまでの列挙は実際には実行されないため、LINQにも注意してください。これは、フィルタリングされたリストを作成したと思ったときに、コンテンツを実際に読んだときに期待した内容とは異なる場合があることを意味します。


1

リストが非常に大きく、繰り返しフィルタリングしている場合は、フィルター属性の元のリストを並べ替えて、バイナリ検索で始点と終点を見つけることができます。

初期時間O(n * log(n))、次にO(log(n))。

標準フィルタリングは毎回O(n)を使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.