2013-06-24 103 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 
} 
相关问题