C # 'daki koleksiyonlara filtre uygulama


142

C # bir koleksiyon filtre için çok hızlı bir yol arıyorum. Şu anda genel Liste <nesne> koleksiyonları kullanıyorum, ancak daha iyi performans gösterirse diğer yapıları kullanmaya açıkım.

Şu anda, sadece yeni bir Liste oluşturuyorum ve orijinal liste boyunca dönüyorum. Filtreleme ölçütleri eşleşirse, yeni listeye bir kopya koyarım.

Bunu yapmanın daha iyi bir yolu var mı? Geçici bir listeye gerek duyulmaması için filtrelemenin bir yolu var mı?


Bu çok hızlı olacak. Sisteminizin yavaşlamasına neden oluyor mu? Bir edilmektedir mı kocaman liste? Aksi halde endişelenmezdim.
Iain Holder

Yanıtlar:


237

C # 3.0 kullanıyorsanız linq'i daha iyi ve daha zarif kullanabilirsiniz:

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();

Bulamıyorsanız .Where, bu using System.Linq;, dosyanızın üst kısmından içe aktarmanız gerektiği anlamına gelir .


19
Where uzantısı yöntemi Liste <T> yerine IEnumerable <T> döndürür. Olmalıdır: myList.Where (x => x> 7) .ToList ()
Rafa Castaneda

1
Bu, dizelere göre filtreleme için nasıl çalışır? "Ch" ile başlayan dizeler listesindeki tüm öğeleri bulmak gibi
joncodo

2
@JonathanO Func'un içindeki yöntemleri kullanabilirsiniz. listOfStrings.Where (s => s.StartsWith ("ch")). ToList ();
Mike G

1
Linq sorgularını nesneleştirmenin bir yolu var mı? Örneğin, kullanmak .Where(predefinedQuery)yerine .Where(x => x > 7)?
XenoRo

2
@AlmightyR: Sadece bir argüman alan bir yöntem olarak tanımlayın. Ör: public bool predefinedQuery(int x) { return x > 7; }. Sonra .Where(predefinedQuery)iyi çalışır.
Don

21

Aşağıda, Lambdas ve LINQ tabanlı liste filtrelemesini göstermek için bir araya getirdiğim üç farklı yöntem kullanarak bazı liste filtrelemenin kod bloğu / örneği verilmiştir.

#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>FindAllfiltrelemeyi sizin için yapacak ve listenin bir alt kümesini döndürecek bir yöntemi vardır.

MSDN'nin burada harika bir kod örneği var: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

DÜZENLEME: Bunu LINQ ve Where()yöntem hakkında iyi bir anlayışa sahip olmadan yazmıştım . Bunu bugün yazacak olsaydım muhtemelen Jorge'nin yukarıda bahsettiği yöntemi kullanırdım. FindAllEğer olsa .NET 2.0 ortamında sıkışmış iseniz yöntemi hala çalışır.


4
Linq iyidir, ancak en az bir büyüklük daha yavaştır, bu nedenle IEnumerable'a güvenmeyen FindAll ve filtreleme uzantı yöntemleri (dizinin bir kısmı vardır), performansın önemli olduğu senaryolar için hala mantıklıdır. (FWIW, Linq ve / veya IEnumerable tarafından ihtiyaç duyulan faktör 7'den 50'ye daha fazla zaman aldım)
Philm

Bunun kabul edilen cevap olmaması için bir neden var mı? Daha hızlı görünüyor ve sözdizimi sonunda daha net (toList ()) çağrısı yok.
Lottem

6

Geçici listeye olan ihtiyacı ortadan kaldırmak için IEnumerable kullanabilirsiniz.

public IEnumerable<T> GetFilteredItems(IEnumerable<T> collection)
{
    foreach (T item in collection)
    if (Matches<T>(item))
    {
        yield return item;
    }
}

Burada Eşleşmeler, filtre yönteminizin adıdır. Ve bunu şöyle kullanabilirsiniz:

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

Bu, gerektiğinde GetFilteredItems işlevini çağırır ve bazı durumlarda filtrelenmiş koleksiyondaki tüm öğeleri kullanmazsanız, bazı iyi performans kazanımları sağlayabilir.


4

Yerinde yapmak için, "List <>" sınıfının özel "Predicate" sınıfıyla birlikte RemoveAll yöntemini kullanabilirsiniz ... ancak tek yapmanız gereken kodu temizlemek ... kaputun altında aynı şeyi yapıyor olduğunuz şey ... ama evet, bunu yerinde yapar, böylece geçici listeyi aynen yaparsınız.



3

C # 3.0 kullanıyorsanız linq kullanabilirsiniz

İsterseniz, C # 3 derleyicisi tarafından sağlanan özel sorgu sözdizimini kullanın:

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

3

LINQ kullanmak, Listeler FindAllyöntemine sağlanan bir yüklem kullanmaktan nispeten daha yavaştır . Ayrıca listsonuca erişene kadar numaralandırma gerçekte yürütülmediğinden , LINQ'ya da dikkat edin . Bu, filtrelenmiş bir liste oluşturduğunuzu düşündüğünüzde, içeriğin gerçekten okuduğunuzda beklediğinizden farklı olabileceği anlamına gelebilir.


1

Listeniz çok büyükse ve tekrar tekrar filtreliyorsanız - başlangıç ​​ve bitiş noktalarını bulmak için orijinal listeyi filtre özelliğine, ikili aramaya göre sıralayabilirsiniz.

Başlangıç ​​zamanı O (n * log (n)) sonra O (log (n)).

Standart filtreleme her seferinde O (n) alacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.