IEnumerable.Count()O(n)

时间:2011-12-05 17:52:31

标签: c# linq ienumerable

我偶然发现了这段代码,我想知道为什么Count在循环中完成。

  /// <summary>
  /// find the first index in a sequence to satisfy a condition
  /// </summary>
  /// <typeparam name="T">type of elements in source</typeparam>
  /// <param name="source">sequence of items</param>
  /// <param name="predicate">condition of item to find</param>
  /// <returns>the first index found, or -1 if not found</returns>
  public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> predicate)
  {
         for (int i = 0; i < source.Count(); i++)
         {
               if (predicate(source.ElementAt(i))) return i;
         }
         return -1; // Not found
  }

如果计数可以改变,我们不应该这样做:for(int i = source.Count() - 1; i&gt; = 0; i - )

否则,我认为我们应该在循环开始之前计算计数,而不是每次都计算。

这样做的正确方法是什么?

4 个答案:

答案 0 :(得分:14)

为此编写手动代码的正确方法是丢失所有垃圾并简单地使用foreach进行迭代:

public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> predicate) 
{
    var index = 0;
    foreach(var item in source) {
        if(predicate(item)) {
            return index;
        }
        ++index;
    }

    return -1;
} 

答案 1 :(得分:3)

这可能是真正错误的代码 - 它可能会迭代所有O(n ^ 2),因为Count()可能需要迭代(如果它没有&# 39;实现ICollection-of-T / IList-of-T提供.Count,经过测试),ElementAt(x)可能需要迭代x项,因为它可能没有IList-of-T用于索引器。它可能适用于列表/数组,但在一般情况下它提供了糟糕的性能。最糟糕的情况是它迭代所有n个项目n次(对于Count()s),再加上伸缩/三角形系列,给出另一个n(n + 1)/ 2个增量(对于ElementAt()s)。

只需使用foreach。或者更好的是,寻找现有的IndexOf方法。

答案 2 :(得分:1)

如果您要使用LINQ,那么为什么不在LINQ中完成所有操作? LINQ的设计特别容易在这种情况下使用。

答案 3 :(得分:0)

这是使用Linq的O(n)方式:

public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> pred)
{
    var foundItem = 
        items
            .Select( (i,index) => new {i,index} )
            .FirstOrDefault( x => pred(x.i));
    return foundItem == null ? -1 : foundItem.index;
}