LINQ-to-objects 中的 Enumerable.Where 是否保留顺序?
var source = new List<string> { "A1", "A2", "B1", "B2" };
var filtered = source.Where(s => s.StartsWith("A"));
foreach (var s in filtered)
Console.WriteLine(s); // outputs first A1 and then A2
它似乎像Enumerable.Where
在有序 IEnumerable(例如 List
或 T[]
)上使用时保留元素的原始顺序。情况总是如此吗?如果是,这在哪里记录?
var source = new List<string> { "A1", "A2", "B1", "B2" };
var filtered = source.Where(s => s.StartsWith("A"));
foreach (var s in filtered)
Console.WriteLine(s); // outputs first A1 and then A2
It seems like Enumerable.Where
keeps the original order of elements when used on an ordered IEnumerable (such as a List<T>
or T[]
). Is this always the case? If yes, where is this documented?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
Microsoft 实际上记录了 LINQ to Objects 保留顺序。该文件
http://msdn.microsoft.com/ en-us/library/dd460677%28v=vs.110%29.aspx 说
正如 这篇 stackoverflow 文章 中提到的一些 LINQ 方法的 Microsoft 文档他们不维护秩序。例如,distinct 文档 提到此方法返回一个 无序序列。
Microsoft does actually document that LINQ to Objects preserves ordering. The document
http://msdn.microsoft.com/en-us/library/dd460677%28v=vs.110%29.aspx says
As mentioned in this stackoverflow article microsoft documents for some LINQ methods that they do not preserve order. For example the documentation of distinct mentions that this method returns an unordered sequence.
使用 Enumerable.Where 方法保留顺序。
SO 上也提出了类似的问题,第一个答案详细说明了哪些方法保留顺序:
Preserving order with LINQ
The order is preserved using the Enumerable.Where method.
There was a similar question asked on SO, and the first answer breaks down which methods preserve the order:
Preserving order with LINQ
Enumerable.Where
将保留IEnumerable
的顺序。这没有记录,因此不能保证,但是,实现只是按顺序迭代IEnumerable
- 实际上,保留顺序 - 尽管该顺序依赖于底层IEnumerable<
。 T> 的顺序。 (例如,Where on a HashSetHashSet
的可枚举性是无序的。)但是,它确实包括处理某些类型(例如数组和 List)的特殊实现,因此在未来的某个时刻,特定的集合类型可能会返回如果这被认为是速度/性能/等方面的有价值的改进,则会导致不同的顺序。该文档从未明确给出订购保证。
Enumerable.Where
will preserve the order of anIEnumerable<T>
. This is not documented, so not a guarantee, however, the implementation just iterates through theIEnumerable<T>
sequentially - in effect, preserving order - though that order is reliant on the underlyingIEnumerable<T>
's order. (For example, Where on aHashSet<T>
is not ordered, but it's because theHashSet<T>
's enumerable is unordered.)It does, however, include special implementations to handle certain types (such as arrays and
List<T>
), so it's not impossible that a specific collection type, at some point in the future, might return results in a different order if this would be deemed a valuable improvement in terms of speed/perf./etc. The documentation never specifically gives an ordering guarantee.Where 实现本质上如下,添加了一些额外的变量检查:
因此,假设yield 不是异步或并行完成的,它将保留顺序。如果您的源是 .AsParallel,则所有的赌注都取决于顺序。
The Where implementation is essentially as follows with some additional variable checking added in:
As a result, assuming yield is not being done asynchronously or in parallel, it will retain the order. If your source is .AsParallel, all bets are off in terms of the order.
这没有记录。 LINQ 运算符的文档在很多方面都严重缺乏,包括何时何地不保留顺序、何时何地不缓冲操作,或者复杂性保证是什么。
在这种情况下,我不介意依赖实现来保持顺序,因为这就是所有实际实现都会做的事情。
我建议阅读 Edulinq Jon Skeet 的系列文章,在实现功能之前,他解释了操作员应该做什么以及不应该做什么。
It's not documented. Documentation of the LINQ operators is seriously lacking in many aspects, including when and when isn't order kept, when and when isn't the operation buffered, or what are the complexity guarantees.
In this case, I don't mind depending on the implementation keeping the order, because that's what all practical implementations will do.
I recommend reading the Edulinq series by Jon Skeet where, before implementing the functionality, he explain what you should and what you should not expected of an operator.
LINQ 的反编译如下:
System.Collections.Generic.List.MoveNext() 的反编译有代码:
将这两者结合使用,您可以看到顺序将被保留。当然微软将来可能会改变它,但是基于.NET 4.0,List.Where将是有序的。
The decompilation of the LINQ where is:
The decompilation of the System.Collections.Generic.List.MoveNext() has the code:
Using these two together you can see that the the order will be preserved. Of course Microsoft could change it in the future, but based on .NET 4.0, List.Where will be ordered.
看起来它必须保持顺序,因为它能够在无限的 IEnumerable 上运行。
但我在任何地方都找不到这个记录。
It seems that it must preserve order, because it is able to function on infinite
IEnumerable
s.I can't find this documented anywhere though.
是的,它将根据迭代器进行排序。由于这不是 PLINQ,因此可枚举项按照可枚举项的元素位置顺序进行迭代 - 因此,如果您为类实现了某种自定义
IEnumerator
,它将按该顺序返回。Yes, it's going to be ordered according to the iterator. Since this isnt PLINQ, the enumerable is iterated over in order of element position of the enumerable - so if you implemented some kind of custom
IEnumerator
for your class, it would return in that ordering.Enumerable.Where
遵循通过在基础集合上调用GetEnumerator
生成的枚举器返回的元素的顺序 - 查看 MSDNList
上的枚举器已记录为遵循索引 行为。Enumerable.Where
follows the ordering of elements returned by the enumerator generated by callingGetEnumerator
on the underlying collection - looking at MSDN the enumerator onList<T>
is documented to follow the indexing behaviour.如果不提供显式的
OrderBy
,Where
方法就无法保证顺序 - 否则,它会被记录为这样做的。Without providing explicit
OrderBy
, theWhere
method cannot guarantee the order - otherwise, it would have been documented as doing such.