C #에서 컬렉션 필터링


142

C #에서 컬렉션을 필터링하는 매우 빠른 방법을 찾고 있습니다. 나는 현재 일반적인 List <object> 컬렉션을 사용하고 있지만 더 나은 성능을 발휘한다면 다른 구조를 사용할 수 있습니다.

현재 새 List <object>를 만들고 원래 목록을 통해 반복하고 있습니다. 필터링 기준이 일치하면 새 목록에 사본을 넣습니다.

더 좋은 방법이 있습니까? 임시 목록이 필요하지 않도록 필터링 할 수있는 방법이 있습니까?


그것은 엄청나게 빠를 것입니다. 시스템 속도가 느려 집니까? A는인가 거대한 목록은? 그렇지 않으면 걱정하지 않습니다.
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

2
@AlmightyR : 하나의 인수를 취하는 메소드로 정의하십시오. 예 : public bool predefinedQuery(int x) { return x > 7; }. 그렇다면 .Where(predefinedQuery)잘 작동합니다.
Don

21

다음은 Lambdas 및 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/en-us/library/aa701359(VS.80).aspx

편집 : 나는 LINQ와 Where()방법을 잘 이해하기 전에 이것을 썼습니다 . 오늘이 글을 쓰려면 위의 호르헤 언급 방법을 사용했을 것입니다. FindAll그래도 .NET 2.0 환경에 갇혀 있으면 이 방법이 여전히 작동합니다.


4
Linq는 훌륭하지만 적어도 1 배 느리므로 IEnumerable에 의존하지 않는 FindAll 및 필터링 확장 메서드 (예 : 배열에는 많은 방법이 있음)는 성능이 중요한 시나리오에 여전히 적합합니다. (FWIW, 일반적으로 Linq 및 / 또는 IEnumerable에서 필요한 요소 7에서 50까지 더 많은 결과를 얻었습니다)
Philm

이것이 허용되지 않는 이유가 있습니까? 더 빠르며 구문이 더 명확합니다 (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"클래스와 함께 사용할 수 있지만 코드를 정리하기 만하면됩니다. 네가 ...하지만 네, 그것은 제 위치에 있으므로 임시 목록과 동일합니다.


4

List 의 FindAll 메서드를 사용하여 필터링 할 대리자를 제공 할 수 있습니다 . 그러나 @ IainMH에 동의하지만 거대한 목록이 아니라면 너무 걱정하지 않아도됩니다.


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.