2013-06-24 148 views
29

給予初始化IEnumerable如何有效地確定IEnumerable是否包含多個元素?

IEnumerable<T> enumerable; 

我想確定它是否有一個以上的元素。我覺得最明顯的方式做到這一點是:

enumerable.Count() > 1 

不過,我相信Count()枚舉整個集合,這是不必要的這種使用情況。例如,如果集合包含非常大量的元素或從外部來源提供其數據,那麼在性能方面可能會非常浪費。

如何在不枚舉超過2個元素的情況下做到這一點?

+0

如果你必須指望它,那麼做點什麼,你最好強迫它評估一個列表或其他集合。 – devshorts

+0

@devshorts,通過*計數*,你的意思是*取決於它的工作*或*使用'Count()'方法*? – Sam

+1

我的意思是,如果你需要找到它上面有多少個元素,那麼根據這個做一些事情,你可以評估它。 'Count'會評估一次,然後如果再次迭代,則必須重新評估一切。與其他'IEnumerable'方法相同,如'Take'或Skip'。這真的取決於數據是什麼。 – devshorts

回答

35

您可以通過合併在System.Linq的擴展方法測試這個在許多方面...兩個簡單的例子都低於:

bool twoOrMore = enumerable.Skip(1).Any(); 
bool twoOrMoreOther = enumerable.Take(2).Count() == 2; 

我更喜歡,因爲一個共同的方式,第一個檢查Count() >= 1是否與Any(),因此我覺得它更具可讀性。

+3

這並不總是有效的,如果這是你丟失了數據的數據流跳過和/或拿走。此外,如果每次對可枚舉進行操作時數據都在發生變化,這意味着您在完成此測試後對數據的評估可能會有所不同。 – devshorts

+0

@devshorts但他只關心計數,而不是實際的數據。 –

+0

@devshorts True。當然,如果需要,您可以將Take(2)結果保存爲變量,但這不是問題中的要求。 –

4

爲了好玩,調用Next()兩次,然後獲得另一個IEnumerable。

或者,爲此特定目標編寫一個小封裝類:EnumerablePrefetcher : IEnumerable<T>嘗試在初始化時獲取指定數量的項目。 @卡梅倫-S的解決方案是簡單,但下面是更有效的

IEnumerable<T> GetItems()方法應採用屈服回報以這種方式

foreach (T item in prefetchedItems) // array of T, prefetched and decided if IEnumerable has at least n elements 
{ 
    yield return item; 
} 
foreach (T item in otherItems) // IEnumerable<T> 
{ 
    yield return item; 
} 
+0

謝謝;我沒有想過預取或緩存'IEnumerable'! – Sam

+2

代碼示例可以表示爲'return prefetchedItems.Concat(otherItems);'雖然上面也是可靠的。 –

+0

@CameronS,我只是實現了'Concat'存在;謝謝!我認爲框架中缺少這樣的功能。 – Sam

0

。我想出了這個基於Enumerable.Count()的方法。 Skip()將始終迭代並且不會短路,以獲得source的計數爲ICollectionICollection<T>類型。

/// <summary> 
/// Returns true if source has at least <paramref name="count"/> elements efficiently. 
/// </summary> 
/// <remarks>Based on int Enumerable.Count() method.</remarks> 
public static bool HasCountOfAtLeast<TSource>(this IEnumerable<TSource> source, int count) 
{ 
    source.ThrowIfArgumentNull("source"); 
    var collection = source as ICollection<TSource>; 
    if (collection != null) 
    { 
     return collection.Count >= count; 
    } 
    var collection2 = source as ICollection; 
    if (collection2 != null) 
    { 
     return collection2.Count >= count; 
    } 
    int num = 0; 
    checked 
    { 
     using (var enumerator = source.GetEnumerator()) 
     { 
      while (enumerator.MoveNext()) 
      { 
       num++; 
       if (num >= count) 
       { 
        return true; 
       } 
      } 
     } 
    } 
    return false; // < count 
} 
相關問題