LINQ First / FirstOrDefault会迭代整个可枚举的吗?

时间:2017-11-28 15:42:54

标签: c# algorithm linq big-o

上下文

我有numbers类型的变量IEnumerable<int> 我想检查数字是否按升序排列。

算法

所以我得到了第一个将它存储在prev中的元素,并希望检查下一个后续数字。

// numbers can contain "10, 20, 60, 50"
IEnumerable<int> numbers = TraverseInOrder(root);
int prev = numbers.FirstOrDefault();
foreach (var curr in numbers.Skip(1))
{
    if (curr < prev) return false;
    prev = curr;
}
return true;

问题 我使用prev设置numbers.FirstOrDefault()的值,并在foreach中跳过一个元素(numbers.Skip(1))以从下一个元素开始。

所以对于以下代码,

numbers.FirstOrDefault()
numbers.Skip(1)

我是否列举numbers两次O(2N)?意义FirstOrDefault迭代整个列表?
- 或 -
它还是O(N)吗? (FirstOrDefault的{​​{1}} + O(N)的O(1)常数时间

3 个答案:

答案 0 :(得分:6)

有点微妙。

Enumerable.FirstOrDefault不会枚举整个序列,因为这样做没有意义。这是the implementation

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    IList<TSource> list = source as IList<TSource>;
    if (list != null) {
        if (list.Count > 0) return list[0];
    }
    else {
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) return e.Current;
        }
    }
    return default(TSource);
}

如果基础序列为IList<T>,您将获得优化版本,否则将启动枚举

现在假设您的IEnumerable<T> 实际是表示数据库查询的实体框架IQueryable<T>。这意味着您的代码仍然会执行两次查询

根据经验,最好避免多次枚举IEnumerable<T>个序列。如果您知道枚举序列很便宜,则应使用更具描述性的类型,例如ICollection<T>IReadOnlyCollection<T>

答案 1 :(得分:2)

我会巧妙地改变你的技巧,这样你就不需要知道了(尽管其他答案都解决了你特别要求的问题):

IEnumerable<int> numbers = TraverseInOrder(root);
int prev = 0;
bool first = true;
foreach (var curr in numbers)
{
    if (!first && curr < prev) return false;
    prev = curr;
    first = false;
}
return true;

现在你知道了(即使没有阅读文档!)这段代码只会迭代一次可枚举。

答案 2 :(得分:0)

FirstOrDefault()不会迭代整个集合。

IEnumerable and order

只是做:

var moreNumbers = numbers.ToList();
// ...stuff with the list.