목록이 비어 있는지 확인하는 “가장 좋은”(속도와 가독성을 모두 고려) 방법은 무엇입니까? 목록이 유형 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();
}
답변
다음과 같이 할 수 있습니다.
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 ()로 대체하는 것입니다.
답변
나는 당신이 정착 한 것처럼 보이는 코드에 하나의 작은 추가를 할 것입니다. 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();
}
답변
LINQ 자체는 어떻게 든 Count () 메서드에 대해 심각한 최적화를 수행해야합니다.
이것이 당신을 놀라게합니까? IList
구현의 경우 Count
단순히 요소 수를 직접 읽고 메서드 Any
를 쿼리하고 IEnumerable.GetEnumerator
인스턴스를 만들고 MoveNext
적어도 한 번 호출 해야한다고 생각합니다 .
/ @Matt 편집 :
IEnumerable의 Count () 확장 메서드가 다음과 같은 작업을 수행한다고 가정 할 수 있습니다.
예, 물론입니다. 이것이 내가 의미하는 바입니다. 실제로 ICollection
대신 사용 IList
하지만 결과는 동일합니다.
답변
방금 간단한 테스트를 작성했습니다.
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 개 이상의 개체를 넣었고 다른 유형과는 반대로 개수가 더 빨랐습니다.)
답변
List.Count
Microsoft 문서에 따르면 O (1)입니다.
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx
그래서 그냥 사용 List.Count == 0
이 쿼리보다 훨씬 빠릅니다
이것은 목록에서 무언가가 추가되거나 제거 될 때마다 업데이트되는 Count라는 데이터 멤버가 있기 때문입니다. 따라서 호출 List.Count
할 때 모든 요소를 반복 할 필요가 없으며 데이터 멤버 만 반환합니다.
답변
두 번째 옵션은 여러 항목이있는 경우 훨씬 더 빠릅니다.
Any()
항목이 1 개 발견되는 즉시 반환됩니다.Count()
전체 목록을 계속 검토해야합니다.
예를 들어 열거 형에 1000 개의 항목이 있다고 가정합니다.
Any()
첫 번째 것을 확인한 다음 true를 반환합니다.Count()
전체 열거를 순회 한 후 1000을 반환합니다.
술어 재정의 중 하나를 사용하는 경우 잠재적으로 더 나빠질 수 있습니다. Count ()는 여전히 일치하는 항목이 하나만 있더라도 모든 단일 항목을 확인해야합니다.
Any one을 사용하는 데 익숙해집니다. 의미가 있고 읽을 수 있습니다.
한 가지주의 사항-IEnumerable이 아닌 List가있는 경우 해당 목록의 Count 속성을 사용하십시오.
답변
@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>
.