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
훨씬 더 나은 혼합하지 마십시오 iscast있지만, 사용 asnull확인 :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.