LINQ로 목록이 비어 있는지 확인


122

목록이 비어 있는지 확인하는 "가장 좋은"(속도와 가독성을 모두 고려) 방법은 무엇입니까? 목록이 유형 IEnumerable<T>이고 Count 속성이없는 경우에도 마찬가지입니다.

지금 나는 이것 사이에 던지고있다 :

if (myList.Count() == 0) { ... }

이:

if (!myList.Any()) { ... }

내 생각에 두 번째 옵션은 첫 번째 항목을 보자 마자 결과를 반환 할 것이기 때문에 더 빠르지 만 두 번째 옵션 (IEnumerable의 경우)은 개수를 반환하기 위해 모든 항목을 방문해야합니다.

즉, 두 번째 옵션이 읽기 쉬운 것처럼 보입니까? 어느 걸 더 선호하십니까? 아니면 빈 목록을 테스트하는 더 좋은 방법을 생각할 수 있습니까?

@lassevk의 응답을 편집 하는 것은 가능한 경우 캐시 된 카운트를 사용하기위한 약간의 런타임 검사와 결합 된 가장 논리적 인 것 같습니다.

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;

    return !list.Any();
}

5
훨씬 더 나은 혼합하지 마십시오 is및 cast있지만, 사용 as및 null확인 :ICollection<T> collection = list as ICollection<T>; if (collection != null) return colllection.Count;
— abatishchev

2
추가 메서드를 작성하는 이유는 무엇입니까? list.Any()과 동등 하지 list.IsEmpty않습니까? 프레임 워크 방법은 최적화되어야합니다. 성능 병목 현상을 발견 한 경우에만 새 방법을 작성하는 것이 좋습니다.
— dbkk 2011-06-18

6
제안 된 구현에 대한 성능을 측정하는 데 귀찮은 사람이 있습니까? 아니면 모두가 아이디어를 내놓고 있습니까?
— Michael Brown

IsEmpty확장 메서드 를 추가하는 .NET Core 클래스 라이브러리에 문제를 제안했습니다 . github.com/dotnet/corefx/issues/35054 원하는 경우 확인하고 투표하고 동의하십시오.
— RyotaMurohoshi

답변:


100

다음과 같이 할 수 있습니다.

public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
    if (source == null)
        return true; // or throw an exception
    return !source.Any();
}

편집 : 기본 소스에 실제로 빠른 Count 속성이 있으면 단순히 .Count 메서드를 사용하는 것이 빠릅니다. 위의 유효한 최적화는 몇 가지 기본 유형을 감지하고 .Any () 접근 방식 대신 .Count 속성을 사용하지만 보장 할 수없는 경우 .Any ()로 대체하는 것입니다.


4
또는 한 줄을 사용하고 return (source == null)? true :! source.Any (); (예외를 던지지 않는 경우)
— Gage

1
예, null에 대한 예외를 던지지 만이라는 두 번째 확장 메서드를 추가합니다 IsNullOrEmpty().
— devuxer 2011

1
public static Boolean IsNullOrEmpty <T> (이 IEnumerable <T> 소스) {return source == null || ! source.Any (); }
— dan

1
요즘 @Gage :return !source?.Any() ?? true;
— ricksmt

@ricksmt 업데이트 해 주셔서 감사합니다! 나는 확실히 그것을 사용할 것입니다!
— Gage

14

나는 당신이 정착 한 것처럼 보이는 코드에 하나의 작은 추가를 할 것입니다. ICollection이것은 쓸모없는 제네릭 클래스 (즉, Queue<T>및 Stack<T>) 에서도 구현되기 때문에 또한 확인하십시오 . 나는 또한 더 관용적이고 더 빠른 것으로 나타났기 때문에 as대신 사용할 것입니다 .is

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }

    var genericCollection = list as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == 0;
    }

    var nonGenericCollection = list as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == 0;
    }

    return !list.Any();
}

1
나는이 대답을 좋아한다. 경고 한 마디는 일부 컬렉션이 NotSupportedException또는 같은 인터페이스를 완전히 구현하지 않으면 예외를 throw한다는 것 NotImplementedException입니다. 내가 사용하고 있던 컬렉션이 Count (누가 알고 ...)에 대한 예외를 던졌다는 것을 알았을 때 코드 예제를 처음 사용했습니다.
— 샘

1
이러한 최적화가 모든 요소를 ​​열거해야하는 Count ()와 같은 메서드에 유용한 이유를 이해합니다. 그러나 Any ()는 최대 하나의 요소 만 열거하면되므로 여기서 요점을 보지 못했습니다. 반면에 추가하는 캐스트와 if 문은 모든 통화에 대해 지불해야하는 고정 비용입니다.
— codymanix

8

LINQ 자체는 어떻게 든 Count () 메서드에 대해 심각한 최적화를 수행해야합니다.

이것이 당신을 놀라게합니까? IList구현의 경우 Count단순히 요소 수를 직접 읽고 메서드 Any를 쿼리하고 IEnumerable.GetEnumerator인스턴스를 만들고 MoveNext적어도 한 번 호출 해야한다고 생각합니다 .

/ @Matt 편집 :

IEnumerable의 Count () 확장 메서드가 다음과 같은 작업을 수행한다고 가정 할 수 있습니다.

예, 물론입니다. 이것이 내가 의미하는 바입니다. 실제로 ICollection대신 사용 IList하지만 결과는 동일합니다.


6

방금 간단한 테스트를 작성했습니다.

 IEnumerable<Object> myList = new List<Object>();

 Stopwatch watch = new Stopwatch();

 int x;

 watch.Start();
 for (var i = 0; i <= 1000000; i++)
 {
    if (myList.Count() == 0) x = i; 
 }
 watch.Stop();

 Stopwatch watch2 = new Stopwatch();

 watch2.Start();
 for (var i = 0; i <= 1000000; i++)
 {
     if (!myList.Any()) x = i;
 }
 watch2.Stop();

 Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
 Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
 Console.ReadLine();

두 번째는 거의 세 배 느립니다. :)

스택이나 배열 또는 다른 시나리오로 스톱워치 테스트를 다시 시도하면 실제로 보이는 목록 유형에 따라 다릅니다. Count가 더 느리다는 것을 증명하기 때문입니다.

따라서 사용중인 목록 유형에 따라 다릅니다.

(단지 지적하자면, 저는 목록에 2000 개 이상의 개체를 넣었고 다른 유형과는 반대로 개수가 더 빨랐습니다.)


12
Enumerable.Count<T>()에 대한 특수 처리가 ICollection<T>있습니다. 기본 목록 이 아닌 다른 것으로 시도하면 상당히 다른 (느린) 결과가 나타날 것으로 예상 됩니다. Any()하지만 거의 동일하게 유지됩니다.
— Marc Gravell

2
나는 Marc와 동의해야합니다. 이것은 정말 공정한 테스트가 아닙니다.
— Dan Tao

에 Enumerable.Any<T>()대한 특별한 처리가없는 이유는 ICollection<T>무엇입니까? 확실히 매개 변수 Any()가없는 사람 도 Count속성을 확인할 수 ICollection<T>있습니까?
— Lukazoid

5

List.CountMicrosoft 문서에 따르면 O (1)입니다.
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

그래서 그냥 사용 List.Count == 0이 쿼리보다 훨씬 빠릅니다

이것은 목록에서 무언가가 추가되거나 제거 될 때마다 업데이트되는 Count라는 데이터 멤버가 있기 때문입니다. 따라서 호출 List.Count할 때 모든 요소를 ​​반복 할 필요가 없으며 데이터 멤버 만 반환합니다.


1
"IEnumerable"이면 아니오입니다. (처음에 IEnumerable에는 "Count"속성이 없으며 Count () 메서드가 있습니다.) "Count ()"를 호출하려면 IEnumerable이 목록의 모든 단일 요소를 검사해야합니다. "Any"는 1 개의 요소를 찾으면 바로 반환됩니다.
— 00jt

데이터 소스에 따라 다릅니다. yield를 사용하여 IEnumerable을 빌드하는 경우 IEnumerable을 탐색하여 크기를 알아야합니다. 따라서 어떤 경우에는 O (1)뿐입니다. 항상 O (1)은 아닙니다.
— TamusJRoyce 2017

3

두 번째 옵션은 여러 항목이있는 경우 훨씬 더 빠릅니다.

  • Any() 항목이 1 개 발견되는 즉시 반환됩니다.
  • Count() 전체 목록을 계속 검토해야합니다.

예를 들어 열거 형에 1000 개의 항목이 있다고 가정합니다.

  • Any() 첫 번째 것을 확인한 다음 true를 반환합니다.
  • Count() 전체 열거를 순회 한 후 1000을 반환합니다.

술어 재정의 중 하나를 사용하는 경우 잠재적으로 더 나빠질 수 있습니다. Count ()는 여전히 일치하는 항목이 하나만 있더라도 모든 단일 항목을 확인해야합니다.

Any one을 사용하는 데 익숙해집니다. 의미가 있고 읽을 수 있습니다.

한 가지주의 사항-IEnumerable이 아닌 List가있는 경우 해당 목록의 Count 속성을 사용하십시오.


Any ()와 Count ()의 차이점은 분명해 보이지만 @crucible의 프로파일 링 코드는 Count ()가 IEnumerable <T>의 특정 구현에 대해 더 빠르다는 것을 나타내는 것 같습니다. List <T>의 경우 목록 크기가 수천 개의 항목이 될 때까지 Count ()보다 빠른 결과를 제공하기 위해 Any ()를 얻을 수 없습니다. LINQ 자체는 어떻게 든 Count () 메서드에 대해 심각한 최적화를 수행해야합니다.
— Matt Hamilton

3

@Konrad 나를 놀라게 한 것은 내 테스트에서 목록을 수락하는 메서드로 전달 IEnumerable<T>하므로 런타임이 .NET 용 Count () 확장 메서드를 호출하여이를 최적화 할 수 없다는 것입니다 IList<T>.

IEnumerable의 Count () 확장 메서드가 다음과 같은 작업을 수행한다고 가정 할 수 있습니다.

public static int Count<T>(this IEnumerable<T> list)
{
    if (list is IList<T>) return ((IList<T>)list).Count;

    int i = 0;
    foreach (var t in list) i++;
    return i;
}

... 즉, IList<T>.

/ EDIT @Konrad +1 mate-당신이 옳다고 생각합니다 ICollection<T>.


1

좋아, 이건 어때?

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable.GetEnumerator().MoveNext();
}

편집 : 누군가 이미이 솔루션을 스케치했다는 것을 깨달았습니다. Any () 메소드가 이것을 할 것이라고 언급되었지만 왜 직접하지 않습니까? 문안 인사


3
그러나 using그렇지 않으면 IDisposable객체를 생성 한 다음 포기 했기 때문에 블록 에 적절하게 묶으면 덜 간결 해집니다 . 물론 이미 존재하는 확장 방법을 사용하고 (정확히이 작업을 수행하는) 변경하면 더 간결 해집니다 return !enumerable.Any().
— Dan Tao

이미 존재하는 메서드를 다시 작성하는 이유는 무엇입니까? 언급했듯이 Any()정확히 동일한 방법을 다른 이름으로 추가하면 혼란 스러울 것입니다.
— Julien N

1

또 다른 아이디어 :

if(enumerable.FirstOrDefault() != null)

그러나 나는 Any () 접근 방식을 더 좋아합니다.


3
첫 번째 요소가 null 인 비어 있지 않은 목록이있는 경우 어떻게합니까?
— Ekevoo

1

이것은 Entity Framework에서 작동하도록하는 데 중요했습니다.

var genericCollection = list as ICollection<T>;

if (genericCollection != null)
{
   //your code 
}

질문에 대한 답은 무엇입니까? 컬렉션은 내부에 요소가없는 동안 null이 될 수 없습니다.
— 마틴 Verjans

0

Count () Linq로 확인하면 데이터베이스에서 "SELECT COUNT (*) .."가 실행되지만 결과에 데이터가 포함되어 있는지 확인해야하므로 Count () 대신 FirstOrDefault ()를 도입하기로 결정했습니다.

전에

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

if (cfop.Count() > 0)
{
    var itemCfop = cfop.First();
    //....
}

후

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

var itemCfop = cfop.FirstOrDefault();

if (itemCfop != null)
{
    //....
}

0
private bool NullTest<T>(T[] list, string attribute)

    {
        bool status = false;
        if (list != null)
        {
            int flag = 0;
            var property = GetProperty(list.FirstOrDefault(), attribute);
            foreach (T obj in list)
            {
                if (property.GetValue(obj, null) == null)
                    flag++;
            }
            status = flag == 0 ? true : false;
        }
        return status;
    }


public PropertyInfo GetProperty<T>(T obj, string str)

    {
        Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
            .GetType().GetProperties().ToList()
            .Find(property => property.Name
            .ToLower() == Column
            .ToLower()).Name.ToString());
        return GetProperty.Compile()(obj, str);
    }

0

다음은 술어를 허용하는 Dan Tao의 대답을 구현 한 것입니다.

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any(predicate);
}

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any();
}

private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
{
    var genericCollection = source as ICollection<TSource>;
    if (genericCollection != null) return genericCollection.Count == 0;
    var nonGenericCollection = source as ICollection;
    if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
    return false;
}

-1
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;

-3

myList.ToList().Count == 0. 그게 다야


1
이것은 끔찍한 생각입니다. ToList ()는 enumerable이 완전히 평가되도록 강제하므로 과용해서는 안됩니다. 대신 .Any ()를 사용하십시오.
— Jon Rea 2015 년

-5

이 확장 방법은 저에게 효과적입니다.

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}

5
이러한 예외 사용을 피하십시오. 위의 코드에서 잘 정의 된 특정 입력 (예 : 빈 열거 형)에 대한 예외가 예상 됩니다. 따라서 그들은 예외가 아니며 규칙입니다. 이는 가독성과 성능에 영향을 미치는이 제어 메커니즘의 남용입니다. 정말 예외적 인 경우에는 예외 사용을 예약하십시오.
— Konrad Rudolph

일반적으로 동의합니다. 그러나 이것은 해당 누락 된 IsEmpty 메서드에 대한 해결 방법입니다. 그리고 저는 해결 방법이 어떤 일을하는 이상적인 방법이 아니라고 주장합니다. 더욱이 특히이 경우 의도는 매우 명확하고 "더러운"코드가 캡슐화되어 잘 정의 된 위치에 숨겨져 있습니다.
— Jonny Dee

3
-1 : 이렇게하려면 ChulioMartinez의 답변 에서처럼 FirstOrDefault ()를 사용하십시오.
— Daniel Rose

3
예외 처리는 성능 효율성이 매우 낮습니다. 그래서 이것은 여기서 최악의 해결책 일 수 있습니다.
— Julien N

"예외는 예외적이어야합니다." -정상적인 프로그램 흐름에 사용하지 마십시오.
— Jon Rea 2015 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.