上下文
我有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)常数时间
答案 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()
不会迭代整个集合。
只是做:
var moreNumbers = numbers.ToList();
// ...stuff with the list.